From fc0ffd1c02d6785f50d9de8accfeed5713208949 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 15:40:45 +0500 Subject: [PATCH 01/62] =?UTF-8?q?fix=20a=20typo=20#4596=20=F0=9F=98=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blenderbim/blenderbim/core/drawing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index b61f469bea..d4f6adab9b 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -145,7 +145,7 @@ def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identificati def rename_reference(ifc, drawing, reference=None, identification=None): - attributes = drawing.generate_reference_attributes(reference, Identifiaction=identification) + attributes = drawing.generate_reference_attributes(reference, Identification=identification) ifc.run("document.edit_reference", reference=reference, attributes=attributes) From 137a3062698ed4c9eed0a0ad4c476b74b6f12291 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 30 Apr 2024 12:17:23 +0500 Subject: [PATCH 02/62] typing --- src/blenderbim/blenderbim/bim/export_ifc.py | 14 +++++++++----- src/blenderbim/blenderbim/bim/import_ifc.py | 2 +- .../blenderbim/bim/module/drawing/operator.py | 1 + .../blenderbim/bim/module/drawing/svgwriter.py | 2 +- .../blenderbim/bim/module/geometry/data.py | 1 + .../blenderbim/bim/module/material/data.py | 1 + .../blenderbim/bim/module/model/product.py | 5 +++-- src/blenderbim/blenderbim/tool/geometry.py | 5 +++++ .../api/geometry/add_representation.py | 18 +++++++++++------- .../ifcopenshell/api/material/add_profile.py | 16 ++++++++++++---- .../api/owner/update_owner_history.py | 5 +++-- .../ifcpatch/recipes/RegenerateGlobalIds.py | 3 ++- 12 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/export_ifc.py b/src/blenderbim/blenderbim/bim/export_ifc.py index 5efaefa4b1..94d457918d 100644 --- a/src/blenderbim/blenderbim/bim/export_ifc.py +++ b/src/blenderbim/blenderbim/bim/export_ifc.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations import os import bpy import json @@ -36,6 +37,7 @@ import blenderbim.core.style from blenderbim.bim.ifc import IfcStore from mathutils import Vector from typing import Union +from logging import Logger class IfcExporter: @@ -163,10 +165,10 @@ class IfcExporter: bpy.ops.bim.update_representation(obj=obj.name) tool.Geometry.record_object_position(obj) - def get_application_name(self): + def get_application_name(self) -> str: return "BlenderBIM" - def get_application_version(self): + def get_application_version(self) -> str: version = ".".join( [ str(x) @@ -184,11 +186,13 @@ class IfcExporter: class IfcExportSettings: def __init__(self): - self.logger = None - self.output_file = None + self.logger: Logger = None + self.output_file: str = None + self.json_version: str = None + self.json_compact: bool = None @staticmethod - def factory(context, output_file, logger): + def factory(context: bpy.types.Context, output_file: str, logger: Logger) -> IfcExportSettings: settings = IfcExportSettings() settings.output_file = output_file settings.logger = logger diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 086827f1d5..c64b9058d4 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -2041,7 +2041,7 @@ class IfcImporter: class IfcImportSettings: def __init__(self): - self.logger = None + self.logger: logging.Logger = None self.input_file = None self.diff_file = None self.should_use_cpu_multiprocessing = True diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 844100c12e..7da481f1b4 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -29,6 +29,7 @@ import subprocess import numpy as np import multiprocessing import ifcopenshell +import ifcopenshell.ifcopenshell_wrapper import ifcopenshell.geom import ifcopenshell.util.selector import ifcopenshell.util.representation diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py index 39de461c70..4f5b40a52f 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py @@ -719,7 +719,7 @@ class SvgWriter: self.svg.text(sheet_id, insert=(text_position[0], text_position[1] + 2.5), class_="ELEVATION", **text_style) ) - def get_reference_and_sheet_id_from_annotation(self, element): + def get_reference_and_sheet_id_from_annotation(self, element: ifcopenshell.entity_instance) -> tuple[str, str]: reference_id = "-" sheet_id = "-" drawing = tool.Drawing.get_annotation_element(element) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/data.py b/src/blenderbim/blenderbim/bim/module/geometry/data.py index 4925a482f4..bbf10b8392 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/data.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/data.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import ifcopenshell.util.element import blenderbim.tool as tool import ifcopenshell.util.placement from mathutils import Vector diff --git a/src/blenderbim/blenderbim/bim/module/material/data.py b/src/blenderbim/blenderbim/bim/module/material/data.py index cfdf3a8058..5e395438c8 100644 --- a/src/blenderbim/blenderbim/bim/module/material/data.py +++ b/src/blenderbim/blenderbim/bim/module/material/data.py @@ -19,6 +19,7 @@ import os import bpy import ifcopenshell +import ifcopenshell.util.element import ifcopenshell.util.doc import ifcopenshell.util.schema import blenderbim.tool as tool diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 8c66e838d9..5da0e79283 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -39,6 +39,7 @@ from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper from . import prop import json +from typing import Any class EnableAddType(bpy.types.Operator, tool.Ifc.Operator): @@ -511,7 +512,7 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings): ) -def ensure_material_assigned(usecase_path, ifc_file, settings): +def ensure_material_assigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None: if usecase_path == "material.assign_material": if not settings.get("material", None): return @@ -550,7 +551,7 @@ def ensure_material_assigned(usecase_path, ifc_file, settings): obj.data.materials.append(IfcStore.get_element(material[0].id())) -def ensure_material_unassigned(usecase_path, ifc_file, settings): +def ensure_material_unassigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None: elements = settings["products"] if elements[0].is_a("IfcElementType"): elements.extend(ifcopenshell.util.element.get_types(elements[0])) diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index 7acd2429ed..eed3d3412b 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -23,9 +23,14 @@ import hashlib import logging import numpy as np import ifcopenshell +import ifcopenshell.api +import ifcopenshell.util.element +import ifcopenshell.util.system import blenderbim.core.tool +import blenderbim.core.drawing import blenderbim.core.style import blenderbim.core.spatial +import blenderbim.core.system import blenderbim.core.geometry import blenderbim.tool as tool import blenderbim.bim.import_ifc diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index b435cf685b..2bd501cb49 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -29,7 +29,7 @@ EPSILON = 1e-6 class Usecase: - def __init__(self, file, **settings): + def __init__(self, file: ifcopenshell.file, **settings): # TODO: This usecase currently depends on Blender's data model self.file = file self.settings = { @@ -58,7 +58,7 @@ class Usecase: for key, value in settings.items(): self.settings[key] = value - def execute(self): + def execute(self) -> ifcopenshell.entity_instance: self.is_manifold = None if ( isinstance(self.settings["geometry"], bpy.types.Mesh) @@ -379,7 +379,7 @@ class Usecase: Axis=self.file.createIfcDirection(polygon.normal), )) - def create_annotation_fill_areas(self, is_2d=False): + def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]: items = [] if self.file.schema != "IFC2X3": points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=is_2d) @@ -391,7 +391,9 @@ class Usecase: items.append(self.file.createIfcAnnotationFillArea(OuterBoundary=curve)) return items - def create_curve_from_polygon(self, points, polygon, is_2d=False): + def create_curve_from_polygon( + self, points: ifcopenshell.entity_instance, polygon: bpy.types.MeshPolygon, is_2d=False + ) -> ifcopenshell.entity_instance: indices = list(polygon.vertices) indices.append(indices[0]) edge_loop = [self.file.createIfcLineIndex((v1 + 1, v2 + 1)) for v1, v2 in zip(indices, indices[1:])] @@ -460,7 +462,7 @@ class Usecase: return False return True - def create_curves(self, should_exclude_faces=False, is_2d=False): + def create_curves(self, should_exclude_faces=False, is_2d=False, ignore_non_loose_edges=False): geom_data = self.settings["geometry"] if isinstance(geom_data, bpy.types.Mesh): @@ -530,7 +532,9 @@ class Usecase: bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) tool.Blender.apply_bmesh(mesh, bm) - def create_curves_from_mesh_ifc2x3(self, should_exclude_faces=False, is_2d=False): + def create_curves_from_mesh_ifc2x3( + self, should_exclude_faces=False, is_2d=False + ) -> list[ifcopenshell.entity_instance]: geom_data = self.settings["geometry"].copy() self.remove_doubles_from_mesh(geom_data) curves = [] @@ -810,7 +814,7 @@ class Usecase: z = self.convert_si_to_unit(z) return self.file.createIfcCartesianPoint((x, y, z)) - def create_cartesian_point_list_from_vertices(self, vertices, is_2d=False): + def create_cartesian_point_list_from_vertices(self, vertices: list[bpy.types.MeshVertex], is_2d=False): if is_2d: return self.file.createIfcCartesianPointList2D([self.convert_si_to_unit(v.co.xy) for v in vertices]) return self.file.createIfcCartesianPointList3D([self.convert_si_to_unit(v.co) for v in vertices]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py index 8b322b71f7..51b622b3f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py @@ -15,10 +15,18 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . +import ifcopenshell +from typing import Optional class Usecase: - def __init__(self, file, profile_set=None, material=None, profile=None): + def __init__( + self, + file: ifcopenshell.file, + profile_set: ifcopenshell.entity_instance, + material: Optional[ifcopenshell.entity_instance] = None, + profile: Optional[ifcopenshell.entity_instance] = None, + ): """Add a new profile item to a profile set A profile item in a profile set represents an extruded 2D profile curve @@ -41,10 +49,10 @@ class Usecase: how to add a profile set. :type profile_set: ifcopenshell.entity_instance.entity_instance :param material: The IfcMaterial that the profile item is made out of. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance.entity_instance, optional :param profile: The IfcProfileDef that represents the 2D cross section of the the profile item. - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance.entity_instance, optional :return: The newly created IfcMaterialProfile :rtype: ifcopenshell.entity_instance.entity_instance @@ -84,7 +92,7 @@ class Usecase: self.file = file self.settings = {"profile_set": profile_set, "material": material, "profile": profile} - def execute(self): + def execute(self) -> ifcopenshell.entity_instance: profiles = list(self.settings["profile_set"].MaterialProfiles or []) profile = self.file.create_entity("IfcMaterialProfile") if self.settings["material"]: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py index 01f39410d9..15c13d98f0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py @@ -21,10 +21,11 @@ import ifcopenshell import ifcopenshell.api import ifcopenshell.api.owner.settings import ifcopenshell.util.element +from typing import Union class Usecase: - def __init__(self, file, element=None): + def __init__(self, file: ifcopenshell.file, element: ifcopenshell.entity_instance): """Updates the owner that is assigned to an object This ensures that the owner is tracked to have modified the object last, @@ -60,7 +61,7 @@ class Usecase: self.file = file self.settings = {"element": element} - def execute(self): + def execute(self) -> Union[ifcopenshell.entity_instance, None]: if not hasattr(self.settings["element"], "OwnerHistory"): return user = ifcopenshell.api.owner.settings.get_user(self.file) diff --git a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py index 58e35cb2c3..9556c38544 100644 --- a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py +++ b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py @@ -17,10 +17,11 @@ # along with IfcPatch. If not, see . import ifcopenshell +from logging import Logger class Patcher: - def __init__(self, src, file, logger, only_duplicates=False): + def __init__(self, src: str, 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 From 0ef683013a01fc7900511df3e23c515fdb4a60b7 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 30 Apr 2024 16:53:35 +0500 Subject: [PATCH 03/62] RegenerateGlobalIds - more informative fixing duplicates --- src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py index 9556c38544..947efbfa1a 100644 --- a/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py +++ b/src/ifcpatch/ifcpatch/recipes/RegenerateGlobalIds.py @@ -54,18 +54,27 @@ class Patcher: def patch(self): if self.only_duplicates: + duplicates = 0 + invalid_ids = 0 + guids = set() for element in self.file.by_type("IfcRoot"): if element.GlobalId in guids: element.GlobalId = ifcopenshell.guid.new() + duplicates += 1 elif len(element.GlobalId) != 22 or element.GlobalId[0] not in "0123": element.GlobalId = ifcopenshell.guid.new() + invalid_ids += 1 else: try: ifcopenshell.guid.expand(element.GlobalId) except: element.GlobalId = ifcopenshell.guid.new() + invalid_ids += 1 guids.add(element.GlobalId) + + print("Replaced %s duplicate GlobalIds" % duplicates) + print("Replaced %s invalid GlobalIds" % invalid_ids) else: for element in self.file.by_type("IfcRoot"): element.GlobalId = ifcopenshell.guid.new() From addcf531f08dab5495b90632ae563050a72b9131 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 30 Apr 2024 16:37:43 +0500 Subject: [PATCH 04/62] owner.update_owner_history - optimization as it may be used very often --- .../api/owner/update_owner_history.py | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py index 15c13d98f0..27230372fb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py @@ -62,7 +62,8 @@ class Usecase: self.settings = {"element": element} def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not hasattr(self.settings["element"], "OwnerHistory"): + element = self.settings["element"] + if not element.is_a("IfcRoot"): return user = ifcopenshell.api.owner.settings.get_user(self.file) if not user: @@ -70,14 +71,24 @@ class Usecase: application = ifcopenshell.api.owner.settings.get_application(self.file) if not application: return - if not self.settings["element"].OwnerHistory: - self.settings["element"].OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", self.file) - return self.settings["element"].OwnerHistory - if self.file.get_total_inverses(self.settings["element"].OwnerHistory) > 1: - new = ifcopenshell.util.element.copy(self.file, self.settings["element"].OwnerHistory) - self.settings["element"].OwnerHistory = new - self.settings["element"].OwnerHistory.ChangeAction = "MODIFIED" - self.settings["element"].OwnerHistory.LastModifiedDate = int(time.time()) - self.settings["element"].OwnerHistory.LastModifyingUser = user - self.settings["element"].OwnerHistory.LastModifyingApplication = application - return self.settings["element"].OwnerHistory + + # 1 IfcRoot IfcOwnerHistory + owner_history = element[1] + if not owner_history: + owner_history = ifcopenshell.api.run("owner.create_owner_history", self.file) + element[1] = owner_history + return owner_history + + if self.file.get_total_inverses(owner_history) > 1: + owner_history = ifcopenshell.util.element.copy(self.file, owner_history) + element[1] = owner_history + + # 3 IfcOwnerHistory ChangeAction + owner_history[3] = "MODIFIED" + # 4 IfcOwnerHistory LastModifiedDate + owner_history[4] = int(time.time()) + # 5 IfcOwnerHistory LastModifyingUser + owner_history[5] = user + # 6 IfcOwnerHistory LastModifyingApplication + owner_history[6] = application + return owner_history From b42f6b182773f56f593e4e2841f9fb2fffd24dc8 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 30 Apr 2024 17:45:37 +0500 Subject: [PATCH 05/62] ifc delete to show time it took if it was more than 10 secs --- src/blenderbim/blenderbim/bim/module/geometry/operator.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index f953c1d743..9dd2b04ea1 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -538,6 +538,8 @@ class OverrideDelete(bpy.types.Operator): row.prop(self, "is_batch", text="Enable Faster Deletion") def _execute(self, context): + start_time = time() + if self.is_batch: ifcopenshell.util.element.batch_remove_deep2(tool.Ifc.get()) @@ -562,6 +564,11 @@ class OverrideDelete(bpy.types.Operator): IfcStore.add_transaction_operation(self) # Required otherwise gizmos are still visible context.view_layer.objects.active = None + + operator_time = time() - start_time + if operator_time > 10: + self.report({"INFO"}, "IFC Delete was finished in {:.2f} seconds".format(operator_time)) + return {"FINISHED"} def rollback(self, data): From 374348bb817278b12a8ee29e5b48019a6b581040 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 30 Apr 2024 18:05:29 +0500 Subject: [PATCH 06/62] IfcImporter small optimization --- src/blenderbim/blenderbim/bim/import_ifc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index c64b9058d4..93ebe4b4ee 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -299,6 +299,7 @@ class IfcImporter: if self.ifc_import_settings.should_setup_viewport_camera: self.setup_viewport_camera() self.setup_arrays() + self.profile_code("Setup arrays") self.update_progress(100) bpy.context.window_manager.progress_end() @@ -602,6 +603,9 @@ class IfcImporter: return products def predict_dense_mesh(self): + if self.ifc_import_settings.should_use_native_meshes: + return + threshold = 10000 # Just from experience. faces = [len(e.CfsFaces) for e in self.file.by_type("IfcClosedShell")] From 0f548eb93e57e6f69c82d7e0ad0a1db8c4f67f83 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 14:40:34 +0500 Subject: [PATCH 07/62] Indicate in UI if type material was overridden by occurrence material Example - https://i.imgur.com/T1hidJg.png +small optimization in material_name --- .../blenderbim/bim/module/material/data.py | 21 +++++++++++++++++-- .../blenderbim/bim/module/material/ui.py | 8 ++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/material/data.py b/src/blenderbim/blenderbim/bim/module/material/data.py index 5e395438c8..69015b1390 100644 --- a/src/blenderbim/blenderbim/bim/module/material/data.py +++ b/src/blenderbim/blenderbim/bim/module/material/data.py @@ -167,6 +167,9 @@ class ObjectMaterialData: cls.data["type_material"] = cls.type_material() cls.data["material_type"] = cls.material_type() cls.data["active_material_constituents"] = cls.active_material_constituents() + # after material_name and type_material + cls.data["is_type_material_overridden"] = cls.is_type_material_overridden() + cls.is_loaded = True @classmethod @@ -295,8 +298,7 @@ class ObjectMaterialData: @classmethod def material_name(cls): - element = tool.Ifc.get_entity(bpy.context.active_object) - material = ifcopenshell.util.element.get_material(element) + material = cls.material if material: return getattr(material, "Name", None) or "Unnamed" @@ -340,3 +342,18 @@ class ObjectMaterialData: if not cls.material or not material.is_a("IfcMaterialConstituentSet"): return [] return [m.Name for m in material.MaterialConstituents if m.Name] + + @classmethod + def is_type_material_overridden(cls) -> bool: + if not cls.data["type_material"]: + return False + + # try to avoid accessing ifc + if cls.data["material_name"] != cls.data["type_material"]: + return True + + # in theory material can be overridden by the same material + # so we check occurrence material explicitly + element = tool.Ifc.get_entity(bpy.context.active_object) + occurrence_material = ifcopenshell.util.element.get_material(element, should_inherit=False) + return bool(occurrence_material) diff --git a/src/blenderbim/blenderbim/bim/module/material/ui.py b/src/blenderbim/blenderbim/bim/module/material/ui.py index 6273596940..c5ee393d8b 100644 --- a/src/blenderbim/blenderbim/bim/module/material/ui.py +++ b/src/blenderbim/blenderbim/bim/module/material/ui.py @@ -177,7 +177,13 @@ class BIM_PT_object_material(Panel): if ObjectMaterialData.data["type_material"]: row = self.layout.row(align=True) - row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="CON_CHILDOF") + if ObjectMaterialData.data["is_type_material_overridden"]: + row.label( + text=f"Inherited Material Is Occurrence Overridden", + icon="CON_CHILDOF", + ) + else: + row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="CON_CHILDOF") if ObjectMaterialData.data["material_class"]: return self.draw_material_ui() From bef5d3cca514db9d7346e15b15357fa1445606bb Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 15:18:12 +0500 Subject: [PATCH 08/62] blenderbim - fix bug with unassigning material When you would unassign material (e.g. implicitly by removing the object), it might have also removed materials slots from other occurrences of the same type. Which may had some unexpected sideffects later on - as unassigned styles saving IFC project (BBIM unassigned styles based on a assumption that it was the user decision to remove the materials and related styles). --- .../blenderbim/bim/module/model/product.py | 78 +++++++++++++++---- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 5da0e79283..7fb235cd2c 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -39,7 +39,7 @@ from mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper from . import prop import json -from typing import Any +from typing import Any, Union class EnableAddType(bpy.types.Operator, tool.Ifc.Operator): @@ -555,23 +555,69 @@ def ensure_material_unassigned(usecase_path: str, ifc_file: ifcopenshell.file, s elements = settings["products"] if elements[0].is_a("IfcElementType"): elements.extend(ifcopenshell.util.element.get_types(elements[0])) + update_blender_ifc_materials(elements) + + +def update_blender_ifc_materials(elements: list[ifcopenshell.entity_instance]) -> None: + """update mesh blender materials that have ifc material connected to them + by replacing them with `blender_material`""" + # since different elements can share meshes (e.g. occurrecnes without openings) + # we need to make sure not to affect them accidentally + meshes_users: dict[bpy.types.Mesh, set[bpy.types.Object]] = dict() + for obj in bpy.data.objects: + if not obj.data: + continue + meshes_users.setdefault(obj.data, set()).add(obj) + + objects: set[bpy.types.Object] = set() for element in elements: - obj = tool.Ifc.get_object(element) + obj: bpy.types.Object = tool.Ifc.get_object(element) if not obj or not obj.data: continue - element_material = ifcopenshell.util.element.get_material(element) - if element_material: + objects.add(obj) + + meshes: set[bpy.types.Mesh] = {obj.data for obj in objects} + + for mesh in meshes: + mesh_users = meshes_users[mesh] + if not mesh_users.issubset(objects): continue - to_remove = [] - for i, slot in enumerate(obj.material_slots): - if not slot.material: + + # NOTE: we need `obj` as removing materials and appending them to `mesh.materials` + # will mess up mesh faces material indices + + # NOTE: we make an assumption here that all mesh users + # have the same material - they either inherit it from the type + # or type doesn't have a material. + # + # If we add option to UI to add materials overriding type materials + # then this assumption won't be safe anymore + + obj = next(iter(mesh_users)) + element = tool.Ifc.get_entity(obj) + current_material = ifcopenshell.util.element.get_material(element) + if current_material: + current_material = tool.Ifc.get_object(current_material) + + material_replaced = False + + for material_slot in obj.material_slots: + material = material_slot.material + if material is None: continue - material = tool.Ifc.get_entity(slot.material) - if material: - to_remove.append(i) - total_removed = 0 - for i in to_remove: - obj.active_material_index = i - total_removed - with bpy.context.temp_override(object=obj): - bpy.ops.object.material_slot_remove() - total_removed += 1 + ifc_material = tool.Ifc.get_entity(material) + # it's blender material for style, so ignore it + if not ifc_material: + continue + if ifc_material == current_material: + continue + material_slot.material = current_material + material_replaced = True + + if not material_replaced and current_material: + mesh.materials.append(current_material) + + # clear empty slots + for i, material in reversed(list(enumerate(mesh.materials[:]))): + if material is None: + mesh.materials.pop(index=i) From 9f12091c7f61f769449af605e36aa658669ef036 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 16:07:20 +0500 Subject: [PATCH 09/62] fix bug assigning materials it was clearing all other blender materials - e.g. blender materials associated with representation items styles. Also had the same issue with affecting extra elements as with unassigning materials --- .../blenderbim/bim/module/model/product.py | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 7fb235cd2c..a862d2b79b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -525,30 +525,7 @@ def ensure_material_assigned(usecase_path: str, ifc_file: ifcopenshell.file, set ]: elements.extend(rel.RelatedObjects) - for element in elements: - obj = IfcStore.get_element(element.GlobalId) - if not obj or not obj.data: - continue - - element_material = ifcopenshell.util.element.get_material(element) - material = [m for m in ifc_file.traverse(element_material) if m.is_a("IfcMaterial")] - - object_material_ids = [ - om.BIMObjectProperties.ifc_definition_id - for om in obj.data.materials - if om is not None and om.BIMObjectProperties.ifc_definition_id - ] - - if material and material[0].id() in object_material_ids: - continue - - if len(obj.data.materials) == 1: - obj.data.materials.clear() - - if not material: - continue - - obj.data.materials.append(IfcStore.get_element(material[0].id())) + update_blender_ifc_materials(elements) def ensure_material_unassigned(usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None: From 45b2411c2bf4cd006f5ba520ce0e35302e2e364b Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 17:38:41 +0500 Subject: [PATCH 10/62] Fix #4598 after 229c7285c --- src/blenderbim/blenderbim/tool/system.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/tool/system.py b/src/blenderbim/blenderbim/tool/system.py index 68a50fb46a..cb0753d60f 100644 --- a/src/blenderbim/blenderbim/tool/system.py +++ b/src/blenderbim/blenderbim/tool/system.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import ifcopenshell.util.element import ifcopenshell.util.system import blenderbim.core.tool import blenderbim.tool as tool @@ -145,7 +146,7 @@ class System(blenderbim.core.tool.System): new.ifc_class = system.is_a() @classmethod - def load_ports(cls, element, ports): + def load_ports(cls, element: ifcopenshell.entity_instance, ports: list[ifcopenshell.entity_instance]) -> None: if not ports: return obj = tool.Ifc.get_object(element) @@ -155,7 +156,13 @@ class System(blenderbim.core.tool.System): ifc_importer.calculate_unit_scale() ifc_importer.process_context_filter() ifc_importer.create_generic_elements(set(ports)) + + container = ifcopenshell.util.element.get_container(element) + if container: + collection = tool.Ifc.get_object(container).BIMObjectProperties.collection + ifc_importer.collections[container.GlobalId] = collection ifc_importer.place_objects_in_collections() + for port_obj in ifc_importer.added_data.values(): port_obj.parent = obj port_obj.matrix_parent_inverse = obj.matrix_world.inverted() From 5410f14aa169d121cc9bc74e690f6323b5d8d610 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 18:03:17 +0500 Subject: [PATCH 11/62] ifc2sql - stringify psets list values #4599 --- src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py index 1bf7b5b9ea..c1940ef5df 100644 --- a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py +++ b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py @@ -450,6 +450,8 @@ class Patcher: for prop_name, value in pset_data.items(): if prop_name == "id": continue + if isinstance(value, list): + value = repr(value) pset_rows.append([element.id(), pset_name, prop_name, value]) if self.should_get_geometry: From 523e51c7756e060161f2fab0aa47422f0ff8e0c0 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 18:26:53 +0500 Subject: [PATCH 12/62] use json serialization for #4599 changed my mind about 5410f14aa, probably better to use `json.dumps` to keep it consistent with how we serialize other attributes --- src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py index c1940ef5df..e51baa7345 100644 --- a/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py +++ b/src/ifcpatch/ifcpatch/recipes/Ifc2Sql.py @@ -451,7 +451,7 @@ class Patcher: if prop_name == "id": continue if isinstance(value, list): - value = repr(value) + value = json.dumps(value) pset_rows.append([element.id(), pset_name, prop_name, value]) if self.should_get_geometry: From be262eaef27aa0965b7a342d53430fd0d1477aaa Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 1 May 2024 18:26:18 +0500 Subject: [PATCH 13/62] fix errors linking ifc2x3 projects it was failing with something very verbose: ``` File "\blenderbim\bim\module\project\operator.py", line 1239, in execute db = ifcpatch.execute( ^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcpatch\__init__.py", line 80, in execute patcher.patch() File "\blenderbim\libs\site\packages\ifcpatch\recipes\ExtractPropertiesToSQLite.py", line 137, in patch properties.append([i, "IFC Material", f"Layer {idx + 1} Name", item.Name]) ^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\entity_instance.py", line 197, in __getattr__ raise AttributeError( AttributeError: entity instance of type 'IFC2X3.IfcMaterialLayer' has no attribute 'Name' Error: Python: Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 1239, in execute db = ifcpatch.execute( ^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcpatch\__init__.py", line 80, in execute patcher.patch() File "\blenderbim\libs\site\packages\ifcpatch\recipes\ExtractPropertiesToSQLite.py", line 137, in patch properties.append([i, "IFC Material", f"Layer {idx + 1} Name", item.Name]) ^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\entity_instance.py", line 197, in __getattr__ raise AttributeError( AttributeError: entity inTraceback (most recent call last): File "\Temp\tmpdggt3syt.py", line 9, in run() File "\Temp\tmpdggt3syt.py", line 5, in run bpy.ops.bim.load_linked_project(filepath="/Dormitory-ARC.ifc", false_origin="0,0,0") File "\Blender\4.1\scripts\modules\bpy\ops.py", line 109, in __call__ ret = _op_call(self.idname_py(), kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: Error: Python: Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 1239, in execute db = ifcpatch.execute( ^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcpatch\__init__.py", line 80, in execute patcher.patch() File "\blenderbim\libs\site\packages\ifcpatch\recipes\ExtractPropertiesToSQLite.py", line 137, in patch properties.append([i, "IFC Material", f"Layer {idx + 1} Name", item.Name]) ^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\entity_instance.py", line 197, in __getattr__ raise AttributeError( AttributeError: entity instance of type 'IFC2X3.IfcMaterialLayer' has no attribute 'Name' Location: \Blender\4.1\scripts\modules\bpy\ops.py:109 ... truncatedUnregistered Snippets Library BMAX Connector - UnRegistred! An error occurred while processing your IFC. Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 949, in execute self.link_ifc() File "\blenderbim\bim\module\project\operator.py", line 991, in link_ifc self.link_blend(blend_filepath) File "\blenderbim\bim\module\project\operator.py", line 953, in link_blend with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): OSError: load: \Dormitory-ARC.ifc.cache.blend failed to open blend file Error: Python: Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 949, in execute self.link_ifc() File "\blenderbim\bim\module\project\operator.py", line 991, in link_ifc self.link_blend(blend_filepath) File "\blenderbim\bim\module\project\operator.py", line 953, in link_blend with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): OSError: load: \Dormitory-ARC.ifc.cache.blend failed to open blend file Location: \Blender\4.1\scripts\modules\bpy\ops.py:109 Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 870, in execute bpy.ops.bim.load_link(filepath=filepath, false_origin=self.false_origin) File "\Blender\4.1\scripts\modules\bpy\ops.py", line 109, in __call__ ret = _op_call(self.idname_py(), kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: Error: Python: Traceback (most recent call last): File "\blenderbim\bim\module\project\operator.py", line 949, in execute self.link_ifc() File "\blenderbim\bim\module\project\operator.py", line 991, in link_ifc self.link_blend(blend_filepath) File "\blenderbim\bim\module\project\operator.py", line 953, in link_blend with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): OSError: load: \Dormitory-ARC.ifc.cache.blend failed to open blend file Location: \Blender\4.1\scripts\modules\bpy\ops.py:109 ``` --- .../recipes/ExtractPropertiesToSQLite.py | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py index f4c1761879..3b7f49de9c 100644 --- a/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py +++ b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py @@ -23,6 +23,7 @@ import json import time import tempfile import ifcopenshell +import ifcopenshell.util.element try: import sqlite3 @@ -106,14 +107,16 @@ class Patcher: relationships = [] id_map = {e.id(): i for i, e in enumerate(elements)} for i, element in enumerate(elements): - rows.append([ - i, - element[0], - element.is_a(), - ifcopenshell.util.element.get_predefined_type(element), - element[2], - element[3], - ]) + rows.append( + [ + i, + element[0], # IfcRoot.GlobalId + element.is_a(), + ifcopenshell.util.element.get_predefined_type(element), + element[2], # IfcRoot.Name + element[3], # IfcRoot.Description + ] + ) psets = ifcopenshell.util.element.get_psets(element, should_inherit=False) for pset_name, pset_data in psets.items(): for prop_name, value in pset_data.items(): @@ -134,27 +137,30 @@ class Patcher: materials = [] elif material.is_a("IfcMaterialLayerSet"): for idx, item in enumerate(material.MaterialLayers): - properties.append([i, "IFC Material", f"Layer {idx + 1} Name", item.Name]) - properties.append([i, "IFC Material", f"Layer {idx + 1} Material", item.Material.Name]) - if getattr(item.Material, "Category"): - properties.append([i, "IFC Material", f"Layer {idx + 1} Category", item.Material.Category]) + material = item.Material + properties.append([i, "IFC Material", f"Layer {idx + 1} Name", getattr(item, "Name", None)]) + properties.append([i, "IFC Material", f"Layer {idx + 1} Material", material.Name]) + if category := getattr(material, "Category", None): + properties.append([i, "IFC Material", f"Layer {idx + 1} Category", category]) elif material.is_a("IfcMaterialProfileSet"): for idx, item in enumerate(material.MaterialProfiles): + material = item.Material properties.append([i, "IFC Material", f"Profile {idx + 1} Name", item.Name]) - properties.append([i, "IFC Material", f"Profile {idx + 1} Material", item.Material.Name]) - if getattr(item.Material, "Category"): - properties.append([i, "IFC Material", f"Profile {idx + 1} Category", item.Material.Category]) + properties.append([i, "IFC Material", f"Profile {idx + 1} Material", material.Name]) + if category := getattr(material, "Category", None): + properties.append([i, "IFC Material", f"Profile {idx + 1} Category", category]) elif material.is_a("IfcMaterialConstituentSet"): for idx, item in enumerate(material.MaterialConstituents): + material = item.Material properties.append([i, "IFC Material", f"Constituent {idx + 1} Name", item.Name]) - properties.append([i, "IFC Material", f"Constituent {idx + 1} Material", item.Material.Name]) - if getattr(item.Material, "Category"): - properties.append([i, "IFC Material", f"Constituent {idx + 1} Category", item.Material.Category]) + properties.append([i, "IFC Material", f"Constituent {idx + 1} Material", material.Name]) + if category := getattr(material, "Category", None): + properties.append([i, "IFC Material", f"Constituent {idx + 1} Category", category]) elif material.is_a("IfcMaterialList"): for idx, material in enumerate(material.Materials): properties.append([i, "IFC Material", f"Material {idx + 1} Name", material.Name]) - if getattr(material, "Category"): - properties.append([i, "IFC Material", f"Material {idx + 1} Category", material.Category]) + if category := getattr(material, "Category", None): + properties.append([i, "IFC Material", f"Material {idx + 1} Category", category]) layers = ifcopenshell.util.element.get_layers(self.file, element) for idx, layer in enumerate(layers): From c7effa3f2547435a3d8a34c84f8b279d9447dfad Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 2 May 2024 12:13:44 +0500 Subject: [PATCH 14/62] More descriptive errors linking ifc files --- .../blenderbim/bim/module/project/operator.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 7449f8bcad..5bc8fab544 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -47,6 +47,7 @@ from mathutils import Vector, Matrix from bpy.app.handlers import persistent from blenderbim.bim.module.project.data import LinksData from blenderbim.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator +from typing import Union class NewProject(bpy.types.Operator): @@ -867,7 +868,16 @@ class LinkIfc(bpy.types.Operator): except: pass # Perhaps on another drive or something new.name = filepath - bpy.ops.bim.load_link(filepath=filepath, false_origin=self.false_origin) + status = bpy.ops.bim.load_link(filepath=filepath, false_origin=self.false_origin) + if status == {"CANCELLED"}: + error_msg = ( + f'Error processing IFC file "{self.filepath}" ' + "was critical and blend file either wasn't saved or wasn't updated. " + "See logs above in system console for details." + ) + print(error_msg) + self.report({"ERROR"}, error_msg) + return {"FINISHED"} print(f"Finished linking {len(files)} IFCs", time.time() - start) return {"FINISHED"} @@ -946,10 +956,12 @@ class LoadLink(bpy.types.Operator): if self.filepath.lower().endswith(".blend"): self.link_blend(filepath) elif self.filepath.lower().endswith(".ifc"): - self.link_ifc() + status = self.link_ifc() + if status: + return status return {"FINISHED"} - def link_blend(self, filepath): + def link_blend(self, filepath: str) -> None: with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): data_to.scenes = data_from.scenes for scene in bpy.data.scenes: @@ -962,7 +974,7 @@ class LoadLink(bpy.types.Operator): link = bpy.context.scene.BIMProjectProperties.links.get(self.filepath) link.is_loaded = True - def link_ifc(self): + def link_ifc(self) -> Union[set[str], None]: blend_filepath = self.filepath + ".cache.blend" h5_filepath = self.filepath + ".cache.h5" @@ -982,11 +994,14 @@ except Exception as e: exit(1) """ + t = time.time() with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as temp_file: temp_file.write(code) run = subprocess.run([bpy.app.binary_path, "-b", "--python", temp_file.name, "--python-exit-code", "1"]) if run.returncode == 1: print("An error occurred while processing your IFC.") + if not os.path.exists(blend_filepath) or os.stat(blend_filepath).st_mtime < t: + return {"CANCELLED"} self.link_blend(blend_filepath) From 86cc2bf39abd0417bb33a6e4942a007616cb1247 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 2 May 2024 16:48:52 +0500 Subject: [PATCH 15/62] fix bug using "trace outlines" for reprsentation in ifc2x3 Mentioned in #4593 The error message was (it was trying to access curves from the original mesh instead of dummy curve object): ``` Error: Python: Traceback (most recent call last): File "\blenderbim\bim\module\geometry\operator.py", line 46, in execute IfcStore.execute_ifc_operator(self, context) File "\blenderbim\bim\ifc.py", line 349, in execute_ifc_operator result = getattr(operator, "_execute")(context) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\bim\module\geometry\operator.py", line 169, in _execute core.add_representation( File "\blenderbim\core\geometry.py", line 52, in add_representation representation = ifc.run( ^^^^^^^^ File "\blenderbim\tool\ifc.py", line 34, in run return ifcopenshell.api.run(command, IfcStore.get_file(), **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\__init__.py", line 172, in run result = usecase_class(ifc_file, **settings).execute() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\geometry\add_representation.py", line 73, in execute return self.create_plan_representation() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\geometry\add_representation.py", line 170, in create_plan_representation return self.create_annotation2d_representation() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\geometry\add_representation.py", line 833, in create_annotation2d_representation items = [self.file.createIfcGeometricCurveSet(self.create_curves(is_2d=True))] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\geometry\add_representation.py", line 492, in create_curves curves = self.create_curves_from_curve_ifc2x3(is_2d=is_2d, curve_object_data=dummy.data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "\blenderbim\libs\site\packages\ifcopenshell\api\geometry\add_representation.py", line 575, in create_curves_from_curve_ifc2x3 for spline in self.settings["geometry"].splines: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'Mesh' object has no attribute 'splines' ``` --- .../ifcopenshell/api/geometry/add_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 2bd501cb49..155bfb2bea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -572,7 +572,7 @@ class Usecase: curve_object_data = self.settings["geometry"] dim = (lambda v: v.xy) if is_2d else (lambda v: v.xyz) results = [] - for spline in self.settings["geometry"].splines: + for spline in curve_object_data.splines: points = spline.bezier_points[:] + spline.points[:] if spline.use_cyclic_u: points.append(points[0]) From 080f325557deddd3eca54c94722c75a78a4e13c6 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Fri, 3 May 2024 00:59:33 -0500 Subject: [PATCH 16/62] extends bim.select_type by selecting multiple types from a selection of objects. Also when the type is selected and turned on, all other types are automatically turned off. https://imgur.com/a/DQUF0ai --- src/blenderbim/blenderbim/bim/import_ifc.py | 6 ++- .../blenderbim/bim/module/model/ui.py | 1 - .../blenderbim/bim/module/type/operator.py | 48 +++++++++++++------ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 93ebe4b4ee..4e9440ad1c 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -1498,7 +1498,11 @@ class IfcImporter: # Occurs when reloading a project pass project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name] - project_collection.children[self.type_collection.name].hide_viewport = True + types_collection = project_collection.children[self.type_collection.name] + types_collection.hide_viewport = False + for obj in types_collection.collection.objects: #turn off all objects inside Types collection. + obj.hide_set(True) + def clean_mesh(self): obj = None diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 367917a3f0..9a174f11ff 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -134,7 +134,6 @@ class LaunchTypeManager(bpy.types.Operator): op = row.operator("bim.rename_type", icon="GREASEPENCIL", text="") op.element = relating_type["id"] op = row.operator("bim.select_type", icon="OBJECT_DATA", text="") - op.relating_type = relating_type["id"] op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="") op.element = relating_type["id"] op = row.operator("bim.remove_type", icon="X", text="") diff --git a/src/blenderbim/blenderbim/bim/module/type/operator.py b/src/blenderbim/blenderbim/bim/module/type/operator.py index 899dfe4ec1..66d8682965 100644 --- a/src/blenderbim/blenderbim/bim/module/type/operator.py +++ b/src/blenderbim/blenderbim/bim/module/type/operator.py @@ -146,21 +146,39 @@ class SelectType(bpy.types.Operator): relating_type: bpy.props.IntProperty() def execute(self, context): - element = tool.Ifc.get().by_id(self.relating_type) - obj = tool.Ifc.get_object(element) - if obj: - try: - tool.Blender.select_and_activate_single_object(context, obj) - except: - self.report({"INFO"}, "Type object is hidden.") - # IfcTypeProducts are only used for annotations and not part of the model interface. - if element.is_a() != "IfcTypeProduct": - try: - context.scene.BIMModelProperties.ifc_class = element.is_a() - context.scene.BIMModelProperties.relating_type_id = str(self.relating_type) - except: - # Potentially our BIM Tool is filtered to a specific element. - pass + selected_objs = context.selected_objects + active_obj = context.active_object + selected_objs.append(active_obj) #update selected_objs so the active_obj is at the end of the list + last_relating_type_obj = None + types_collection = bpy.data.collections.get("Types") + for obj in types_collection.objects: + obj.hide_set(True) + for obj in selected_objs: + element = tool.Ifc.get_entity(obj) + relating_type = ifcopenshell.util.element.get_type(element) + relating_type_obj = tool.Ifc.get_object(relating_type) + obj.select_set(False) + if relating_type_obj: + if relating_type_obj.hide_get(): + relating_type_obj.hide_set(False) + relating_type_obj.select_set(True) + last_relating_type_obj = relating_type_obj + + context.view_layer.objects.active = last_relating_type_obj #make the active_obj's type the active object + + # if relating_type_obj: + # try: + # tool.Blender.select_and_activate_single_object(context, relating_type_obj) + # except: + # self.report({"INFO"}, "Type object is hidden.") + # # IfcTypeProducts are only used for annotations and not part of the model interface. + # if element.is_a() != "IfcTypeProduct": + # try: + # context.scene.BIMModelProperties.ifc_class = element.is_a() + # context.scene.BIMModelProperties.relating_type_id = str(relating_type) + # except: + # # Potentially our BIM Tool is filtered to a specific element. + # pass return {"FINISHED"} From 0384de46a4cee39f68b98493a56c67639c891103 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 16:21:08 +1000 Subject: [PATCH 17/62] Bump pydantic from 1.10.7 to 1.10.13 in /src/opencdeserver/api/app (#4585) Bumps [pydantic](https://github.com/pydantic/pydantic) from 1.10.7 to 1.10.13. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v1.10.7...v1.10.13) --- updated-dependencies: - dependency-name: pydantic dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/opencdeserver/api/app/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opencdeserver/api/app/requirements.txt b/src/opencdeserver/api/app/requirements.txt index 6ecccc1cba..5851135638 100644 --- a/src/opencdeserver/api/app/requirements.txt +++ b/src/opencdeserver/api/app/requirements.txt @@ -3,7 +3,7 @@ httpx==0.24.1 jose==1.0.0 jsonpickle==3.0.1 passlib==1.7.4 -pydantic==1.10.7 +pydantic==1.10.13 python_dateutil==2.8.2 python_jose==3.3.0 py2neo==2021.2.4 From 25fff3fdd01dfd167ae84d6b0c86d12653ea0bc6 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 3 May 2024 17:01:54 +1000 Subject: [PATCH 18/62] Consolidate duplicate operators "Convert to Blender file" and "Purge IFC Links" --- .../blenderbim/bim/module/debug/__init__.py | 1 - .../blenderbim/bim/module/debug/operator.py | 29 ++++--------------- .../blenderbim/bim/module/debug/ui.py | 3 -- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/debug/__init__.py b/src/blenderbim/blenderbim/bim/module/debug/__init__.py index 271950ea61..8790917a4a 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/debug/__init__.py @@ -35,7 +35,6 @@ classes = ( operator.PrintUnusedElementStats, operator.ProfileImportIFC, operator.PurgeHdf5Cache, - operator.PurgeIfcLinks, operator.PurgeUnusedElementsByClass, operator.RewindInspector, operator.SelectExpressFile, diff --git a/src/blenderbim/blenderbim/bim/module/debug/operator.py b/src/blenderbim/blenderbim/bim/module/debug/operator.py index c97791b6ca..b6605d9712 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/operator.py +++ b/src/blenderbim/blenderbim/bim/module/debug/operator.py @@ -104,10 +104,11 @@ class PrintIfcFile(bpy.types.Operator): return {"FINISHED"} -class PurgeIfcLinks(bpy.types.Operator): - bl_idname = "bim.purge_ifc_links" - bl_label = "Purge IFC Links" - bl_description = "Purge all definitions and references from the file.\nWarning : Cannot be undone." +class ConvertToBlender(bpy.types.Operator): + bl_idname = "bim.convert_to_blender" + bl_label = "Convert To Blender File" + bl_description = "Removes all IFC data and revert to basic Blender objects.\nWarning : Cannot be undone." + bl_options = {"REGISTER", "UNDO"} def execute(self, context): for obj in bpy.data.objects: @@ -124,26 +125,6 @@ class PurgeIfcLinks(bpy.types.Operator): return {"FINISHED"} -class ConvertToBlender(bpy.types.Operator): - bl_idname = "bim.convert_to_blender" - bl_label = "Convert To Blender File" - bl_description = "Removes all IFC data, and converts the file to a simple Blender file." - bl_options = {"REGISTER", "UNDO"} - - def execute(self, context): - for o in bpy.data.objects: - if o.type in {"MESH", "EMPTY"}: - o.BIMObjectProperties.ifc_definition_id = 0 - if o.data: - o.data.BIMMeshProperties.ifc_definition_id = 0 - for m in bpy.data.materials: - m.BIMMaterialProperties.ifc_style_id = False - bpy.context.scene.BIMProperties.ifc_file = "" - IfcStore.purge() - blenderbim.bim.handler.refresh_ui_data() - return {"FINISHED"} - - class ValidateIfcFile(bpy.types.Operator): bl_idname = "bim.validate_ifc_file" bl_label = "Validate IFC File" diff --git a/src/blenderbim/blenderbim/bim/module/debug/ui.py b/src/blenderbim/blenderbim/bim/module/debug/ui.py index 2fd503cf6f..ec296b47d6 100644 --- a/src/blenderbim/blenderbim/bim/module/debug/ui.py +++ b/src/blenderbim/blenderbim/bim/module/debug/ui.py @@ -60,9 +60,6 @@ class BIM_PT_debug(Panel): row = layout.row() row.operator("bim.purge_hdf5_cache") - row = layout.row() - row.operator("bim.purge_ifc_links") - row = layout.row() row.operator("bim.update_representation", text="Manually Save Representation") From 16b0b1c3a52519f1cd9cb8c4945b905b8fc0a1c0 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 15:39:37 +0500 Subject: [PATCH 19/62] Fix UI error #4575 --- src/blenderbim/blenderbim/bim/module/geometry/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/data.py b/src/blenderbim/blenderbim/bim/module/geometry/data.py index bbf10b8392..baf9f356ae 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/data.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/data.py @@ -223,7 +223,7 @@ class ConnectionsData: @classmethod def is_connection_realization(cls): element = tool.Ifc.get_entity(bpy.context.active_object) - connections = element.IsConnectionRealization + connections = getattr(element, "IsConnectionRealization", None) if not connections: return From 93f8638a947d81f041e89e63eb47ee8fa2f3ea00 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 16:00:27 +0500 Subject: [PATCH 20/62] Use active camera drawing id for bim.create_drawing #4581 There was inconsistency between .poll and .execute on what use as active drawing. --- src/blenderbim/blenderbim/bim/module/drawing/operator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 7da481f1b4..4305ef390f 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -220,11 +220,12 @@ class CreateDrawing(bpy.types.Operator): def execute(self, context): self.props = context.scene.DocProperties + active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id if self.print_all: - original_drawing_id = self.props.active_drawing_id + original_drawing_id = active_drawing_id drawings_to_print = [d.ifc_definition_id for d in self.props.drawings if d.is_selected and d.is_drawing] else: - drawings_to_print = [self.props.active_drawing_id] + drawings_to_print = [active_drawing_id] for drawing_i, drawing_id in enumerate(drawings_to_print): self.drawing_index = drawing_i From 92426a640ab45268cdd296f546a8c6a066ae1dff Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 16:23:39 +0500 Subject: [PATCH 21/62] bim.active_model not to unhide all types and drawings elements --- .../blenderbim/bim/module/drawing/operator.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 4305ef390f..41f1c5568d 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -1453,13 +1453,19 @@ class ActivateModel(bpy.types.Operator): CutDecorator.uninstall() + # save current visibility statuses for Views and Types collections + visibility_status: dict[bpy.types.Object, bool] = {} + for col in bpy.data.collections["Views"].children: + for obj in col.objects: + visibility_status[obj] = obj.hide_get() + for obj in bpy.data.collections["Types"].objects: + visibility_status[obj] = obj.hide_get() + if not bpy.app.background: with context.temp_override(**tool.Blender.get_viewport_context()): bpy.ops.object.hide_view_clear() bpy.ops.bim.activate_status_filters() - subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") - for obj in context.visible_objects: element = tool.Ifc.get_entity(obj) if not element: @@ -1477,6 +1483,11 @@ class ActivateModel(bpy.types.Operator): is_global=True, should_sync_changes_first=True, ) + + # restore visibility after hide_view_clear() + for obj, hide_status in visibility_status.items(): + obj.hide_set(hide_status) + tool.Blender.update_viewport() return {"FINISHED"} From a973b62918361b78798730844278d85dd49a8dc7 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 14:16:50 +0500 Subject: [PATCH 22/62] small comment fix --- src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index dcafff9e88..68da4c0da5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -212,7 +212,7 @@ class Usecase: objects_without_types.append(object) continue - # either is_nested_by is None or product is part of different rel + # either rel doesn't exist or product is part of different rel if object_rel != types: previous_types_rels.add(object_rel) objects_with_types.append(object) From de690a385c2541be23993a5024bb0e9ce1069a61 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 14:37:59 +0500 Subject: [PATCH 23/62] project.assign_declaration - support batching #4474 --- .../blenderbim/bim/module/project/operator.py | 2 +- src/blenderbim/scripts/generate_au_library.py | 10 +-- .../scripts/generate_demo_library.py | 8 +- .../scripts/generate_entourage_library.py | 4 +- .../scripts/generate_furniture_library.py | 12 +-- .../scripts/generate_landscape_library.py | 6 +- .../scripts/generate_site_library.py | 4 +- .../generate_steel_profiles_library.py | 4 +- .../scripts/shape_builder_examples.py | 8 +- .../ifcopenshell/api/__init__.py | 3 + .../ifcopenshell/api/project/append_asset.py | 4 +- .../api/project/assign_declaration.py | 80 ++++++++++++------- .../api/project/unassign_declaration.py | 2 +- .../ifcopenshell/api/resource/add_resource.py | 2 +- .../api/sequence/add_work_calendar.py | 2 +- .../api/sequence/add_work_plan.py | 2 +- .../api/sequence/add_work_schedule.py | 2 +- .../api/project/test_assign_declaration.py | 70 ++++++++++++++++ src/ifcopenshell-python/test/api/test_api.py | 27 +++++-- 19 files changed, 182 insertions(+), 70 deletions(-) create mode 100644 src/ifcopenshell-python/test/api/project/test_assign_declaration.py diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 5bc8fab544..68c562c1c0 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -302,7 +302,7 @@ class AssignLibraryDeclaration(bpy.types.Operator): ifcopenshell.api.run( "project.assign_declaration", self.file, - definition=self.file.by_id(self.definition), + definitions=[self.file.by_id(self.definition)], relating_context=self.file.by_type("IfcProjectLibrary")[0], ) element_name = self.props.active_library_element diff --git a/src/blenderbim/scripts/generate_au_library.py b/src/blenderbim/scripts/generate_au_library.py index e75cf739b0..3e20808f75 100644 --- a/src/blenderbim/scripts/generate_au_library.py +++ b/src/blenderbim/scripts/generate_au_library.py @@ -40,7 +40,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="Australian Library" ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) @@ -196,7 +196,7 @@ class LibraryGenerator: ) layer.Name = layer_data[0] layer.LayerThickness = layer_data[2] - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_layer_type(self, ifc_class, name, thickness): @@ -205,7 +205,7 @@ class LibraryGenerator: layer_set = rel.RelatingMaterial layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=layer_set, material=self.materials["TBD"]["ifc"]) layer.LayerThickness = thickness - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_profile_type(self, ifc_class, name, profile): @@ -216,7 +216,7 @@ class LibraryGenerator: "material.add_profile", self.file, profile_set=profile_set, material=self.materials["TBD"]["ifc"] ) ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) def create_type(self, ifc_class, name, representations): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name) @@ -248,7 +248,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) LibraryGenerator().generate() diff --git a/src/blenderbim/scripts/generate_demo_library.py b/src/blenderbim/scripts/generate_demo_library.py index 14f97bc52d..15fda40220 100644 --- a/src/blenderbim/scripts/generate_demo_library.py +++ b/src/blenderbim/scripts/generate_demo_library.py @@ -35,7 +35,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library" ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"}) model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") @@ -209,7 +209,7 @@ class LibraryGenerator: layer_set = rel.RelatingMaterial layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=layer_set, material=self.material) layer.LayerThickness = thickness - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_profile_type(self, ifc_class, name, profile): @@ -220,7 +220,7 @@ class LibraryGenerator: "material.add_profile", self.file, profile_set=profile_set, material=self.material ) ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) def create_type(self, ifc_class, name, representations): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name) @@ -252,7 +252,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) LibraryGenerator().generate() diff --git a/src/blenderbim/scripts/generate_entourage_library.py b/src/blenderbim/scripts/generate_entourage_library.py index a84c73e4ef..07ddcf7e01 100644 --- a/src/blenderbim/scripts/generate_entourage_library.py +++ b/src/blenderbim/scripts/generate_entourage_library.py @@ -42,7 +42,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) @@ -131,7 +131,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) if __name__ == "__main__": diff --git a/src/blenderbim/scripts/generate_furniture_library.py b/src/blenderbim/scripts/generate_furniture_library.py index 6d38170ed0..9a4c12492f 100644 --- a/src/blenderbim/scripts/generate_furniture_library.py +++ b/src/blenderbim/scripts/generate_furniture_library.py @@ -37,7 +37,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) @@ -1797,7 +1797,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation_2d ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_layer_set_type(self, name, data): @@ -1811,7 +1811,7 @@ class LibraryGenerator: ) layer.Name = layer_data[0] layer.LayerThickness = layer_data[2] - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_layer_type(self, ifc_class, name, thickness): @@ -1822,7 +1822,7 @@ class LibraryGenerator: "material.add_layer", self.file, layer_set=layer_set, material=self.materials["TBD"]["ifc"] ) layer.LayerThickness = thickness - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_profile_type(self, ifc_class, name, profile): @@ -1837,7 +1837,7 @@ class LibraryGenerator: # material=self.materials["TBD"]["ifc"] ) ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) def create_type(self, ifc_class, name, representations): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name) @@ -1869,7 +1869,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) if __name__ == "__main__": diff --git a/src/blenderbim/scripts/generate_landscape_library.py b/src/blenderbim/scripts/generate_landscape_library.py index be7bff7b69..06cec8c6df 100644 --- a/src/blenderbim/scripts/generate_landscape_library.py +++ b/src/blenderbim/scripts/generate_landscape_library.py @@ -319,7 +319,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=library_name ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit]) @@ -447,7 +447,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation_2d ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) return element def create_type(self, ifc_class, name, representations): @@ -480,7 +480,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) diff --git a/src/blenderbim/scripts/generate_site_library.py b/src/blenderbim/scripts/generate_site_library.py index ba07c77dc8..73a9ad96a7 100644 --- a/src/blenderbim/scripts/generate_site_library.py +++ b/src/blenderbim/scripts/generate_site_library.py @@ -35,7 +35,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library" ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.library + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.library ) ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"}) model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") @@ -98,7 +98,7 @@ class LibraryGenerator: ifcopenshell.api.run( "geometry.assign_representation", self.file, product=element, representation=representation ) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) LibraryGenerator().generate() diff --git a/src/blenderbim/scripts/generate_steel_profiles_library.py b/src/blenderbim/scripts/generate_steel_profiles_library.py index 685435660f..7eeabbd5ce 100644 --- a/src/blenderbim/scripts/generate_steel_profiles_library.py +++ b/src/blenderbim/scripts/generate_steel_profiles_library.py @@ -43,7 +43,7 @@ class LibraryGenerator: "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name=f"{parse_profiles_type} Steel Profiles Library" ) ifcopenshell.api.run( - "project.assign_declaration", self.file, definition=self.library, relating_context=self.project + "project.assign_declaration", self.file, definitions=[self.library], relating_context=self.project ) dim_exponents = self.file.createIfcDimensionalExponents(0, 0, 0, 0, 0, 0, 0) length_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI") @@ -182,7 +182,7 @@ class LibraryGenerator: # material=self.materials["TBD"]["ifc"] ) ifcopenshell.api.run("material.assign_profile", self.file, material_profile=material_profile, profile=profile) - ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library) + ifcopenshell.api.run("project.assign_declaration", self.file, definitions=[element], relating_context=self.library) def create_double_l_profile(self, profile, resulting_profile_name=None, profiles_gap=0, mode = "LLBB"): def create_derived_profile(profile, mirrored=False): diff --git a/src/blenderbim/scripts/shape_builder_examples.py b/src/blenderbim/scripts/shape_builder_examples.py index f8ba737e74..6561f0badc 100644 --- a/src/blenderbim/scripts/shape_builder_examples.py +++ b/src/blenderbim/scripts/shape_builder_examples.py @@ -91,7 +91,7 @@ def mirror_placement_test(): library = ifcopenshell.api.run( "root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name=f"Non-structural assets library" ) - ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=library, relating_context=project) + ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[library], relating_context=project) unit = ifcopenshell.api.run("unit.add_si_unit", ifc_file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", ifc_file, units=[unit]) model = ifcopenshell.api.run("context.add_context", ifc_file, context_type="Model") @@ -152,7 +152,7 @@ def mirror_placement_test(): element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcFurnitureType", name="test") ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=element, representation=representation_3d) - ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library) + ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[element], relating_context=library) ifc_file.write("tmp.ifc") @@ -165,7 +165,7 @@ def curve_between_two_points_test(): library = ifcopenshell.api.run( "root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name=f"Non-structural assets library" ) - ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=library, relating_context=project) + ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[library], relating_context=project) unit = ifcopenshell.api.run("unit.add_si_unit", ifc_file, unit_type="LENGTHUNIT", prefix="MILLI") ifcopenshell.api.run("unit.assign_unit", ifc_file, units=[unit]) model = ifcopenshell.api.run("context.add_context", ifc_file, context_type="Model") @@ -217,7 +217,7 @@ def curve_between_two_points_test(): print(representation_2d) element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcFurnitureType", name="test") ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=element, representation=representation_2d) - ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library) + ifcopenshell.api.run("project.assign_declaration", ifc_file, definitions=[element], relating_context=library) ifc_file.write("tmp.ifc") diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index f5a56279c0..1d21eaf40f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -117,6 +117,9 @@ ARGUMENTS_DEPRECATION = { "constraint.unassign_constraint": partial( batching_argument_deprecation, prev_argument="product", new_argument="products" ), + "project.assign_declaration": partial( + batching_argument_deprecation, prev_argument="definition", new_argument="definitions" + ), } diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index 4bef25ee75..b3640ca53e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -61,7 +61,7 @@ class Usecase: root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") context = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProjectLibrary", name="Demo Library") - ifcopenshell.api.run("project.assign_declaration", library, definition=context, relating_context=root) + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) # Assign units for our example library unit = ifcopenshell.api.run("unit.add_si_unit", library, @@ -80,7 +80,7 @@ class Usecase: # Mark our wall type as a reusable asset in our library. ifcopenshell.api.run("project.assign_declaration", library, - definition=wall_type, relating_context=context) + definitions=[wall_type], relating_context=context) # Let's imagine we're starting a new project model = ifcopenshell.api.run("project.create_file") diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py index c11e84be9a..be4d076de0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -18,11 +18,18 @@ import ifcopenshell import ifcopenshell.api +import ifcopenshell.util.element +from typing import Union class Usecase: - def __init__(self, file, definition=None, relating_context=None): - """Declares an element to the project + def __init__( + self, + file: ifcopenshell.entity_instance, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, + ): + """Declares the list of elements to the project All data in a model must be directly or indirectly related to the project. Most data is indirectly related, existing instead within the @@ -35,13 +42,14 @@ class Usecase: project libraries for future use (such as an assets library). Assigning a declaration lets you say that an object belongs to a library. - :param definition: The object you want to declare. Typically an asset. - :type definition: ifcopenshell.entity_instance.entity_instance + :param definitions: The list of objects you want to declare. Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance.entity_instance] :param relating_context: The IfcProject, or more commonly the IfcProjectLibrary that you want the object to be part of. :type relating_context: ifcopenshell.entity_instance.entity_instance - :return: The new IfcRelDeclares relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :return: The new IfcRelDeclares relationship or None if all definitions + were already declared / do not support declaration. + :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] Example: @@ -54,7 +62,7 @@ class Usecase: ifc_class="IfcProjectLibrary", name="Demo Library") # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definition=context, relating_context=root) + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) # Assign units for our example library unit = ifcopenshell.api.run("unit.add_si_unit", library, @@ -73,45 +81,61 @@ class Usecase: # Mark our wall type as a reusable asset in our library. ifcopenshell.api.run("project.assign_declaration", library, - definition=wall_type, relating_context=context) + definitions=[wall_type], relating_context=context) # All done, just for fun let's save our asset library to disk for later use. library.write("/path/to/my-library.ifc") """ self.file = file self.settings = { - "definition": definition, + "definitions": definitions, "relating_context": relating_context, } - def execute(self): - declares = None - if self.settings["relating_context"].Declares: - declares = self.settings["relating_context"].Declares[0] + def execute(self) -> Union[ifcopenshell.entity_instance, None]: + relating_context = self.settings["relating_context"] + all_declares = relating_context.Declares + definitions = set(self.settings["definitions"]) - if not hasattr(self.settings["definition"], "HasContext"): - return + previous_declares_rels: set[ifcopenshell.entity_instance] = set() + objects_without_contexts: list[ifcopenshell.entity_instance] = [] + objects_with_contexts: list[ifcopenshell.entity_instance] = [] - has_context = None - if self.settings["definition"].HasContext: - has_context = self.settings["definition"].HasContext[0] + # check if there is anything to change + for definition in definitions: + has_context = getattr(definition, "HasContext", None) + if has_context is None: + continue - if has_context and has_context == declares: - return + object_rel = next(iter(has_context), None) + if object_rel is None: + objects_without_contexts.append(definition) + continue - if has_context: - related_definitions = list(has_context.RelatedDefinitions) - related_definitions.remove(self.settings["definition"]) + # either rel doesn't exist or product is part of different rel + if object_rel not in all_declares: + previous_declares_rels.add(object_rel) + objects_with_contexts.append(definition) + + objects_to_change = objects_without_contexts + objects_with_contexts + # nothing to change + if not objects_to_change: + return None + + for has_context in previous_declares_rels: + related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts if related_definitions: has_context.RelatedDefinitions = related_definitions ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_context}) else: + history = has_context.OwnerHistory self.file.remove(has_context) + if history: + ifcopenshell.util.element.remove_deep2(self.file, history) + declares = next(iter(all_declares), None) if declares: - related_definitions = set(declares.RelatedDefinitions) - related_definitions.add(self.settings["definition"]) - declares.RelatedDefinitions = list(related_definitions) + declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change)) ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": declares}) else: declares = self.file.create_entity( @@ -119,8 +143,8 @@ class Usecase: **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedDefinitions": [self.settings["definition"]], - "RelatingContext": self.settings["relating_context"], + "RelatedDefinitions": list(objects_to_change), + "RelatingContext": relating_context, } ) return declares diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py index 25f3bdf64c..7e93f558fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -46,7 +46,7 @@ class Usecase: ifc_class="IfcProjectLibrary", name="Demo Library") # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definition=context, relating_context=root) + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) # Remove the library from our project ifcopenshell.api.run("project.unassign_declaration", library, definition=context, relating_context=root) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py index 9580389ab6..f2c3bf99ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py @@ -105,7 +105,7 @@ class Usecase: ifcopenshell.api.run( "project.assign_declaration", self.file, - definition=resource, + definitions=[resource], relating_context=context, ) return resource diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index 8823c3f215..9df45247c5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -92,7 +92,7 @@ class Usecase: ifcopenshell.api.run( "project.assign_declaration", self.file, - definition=work_calendar, + definitions=[work_calendar], relating_context=context, ) return work_calendar diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py index 76d2133494..4e907a0ad5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py @@ -84,7 +84,7 @@ class Usecase: ifcopenshell.api.run( "project.assign_declaration", self.file, - definition=work_plan, + definitions=[work_plan], relating_context=context, ) return work_plan diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index 3da4d3b10e..f50745471f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -118,7 +118,7 @@ class Usecase: ifcopenshell.api.run( "project.assign_declaration", self.file, - definition=work_schedule, + definitions=[work_schedule], relating_context=context, ) return work_schedule diff --git a/src/ifcopenshell-python/test/api/project/test_assign_declaration.py b/src/ifcopenshell-python/test/api/project/test_assign_declaration.py new file mode 100644 index 0000000000..ea9f7c1fe1 --- /dev/null +++ b/src/ifcopenshell-python/test/api/project/test_assign_declaration.py @@ -0,0 +1,70 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +# NOTE: supported only in IFC4+ +class TestAssignDeclaration(test.bootstrap.IFC4): + def get_declared_definitions(self, project: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]: + definitions = set() + for declares in project.Declares: + definitions.update(declares.RelatedDefinitions) + return definitions + + def test_assign_a_declaration(self): + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + ifcopenshell.api.run( + "project.assign_declaration", + self.file, + definitions=[element_type, element_type2], + relating_context=library, + ) + assert self.get_declared_definitions(library) == {element_type, element_type2} + assert len(self.file.by_type("IfcRelDeclares")) == 1 + + def test_doing_nothing_if_the_library_is_already_assigned(self): + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type, element_type2], relating_context=library + ) + total_elements = len([e for e in self.file]) + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type, element_type2], relating_context=library + ) + assert len([e for e in self.file]) == total_elements + + def test_that_old_relationships_are_updated_if_they_still_contain_elements(self): + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type], relating_context=library + ) + rel = self.file.by_type("IfcRelDeclares")[0] + + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type2, element_type3], relating_context=library + ) + assert len(rel.RelatedDefinitions) == 3 diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index 72d20392f3..2f4db8786f 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -303,11 +303,26 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): def test_unassigning_a_constraint(self): constraint = ifcopenshell.api.run("constraint.add_objective", self.file) element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run( - "constraint.assign_constraint", self.file, product=element, constraint=constraint - ) - ifcopenshell.api.run( - "constraint.unassign_constraint", self.file, product=element, constraint=constraint - ) + ifcopenshell.api.run("constraint.assign_constraint", self.file, product=element, constraint=constraint) + ifcopenshell.api.run("constraint.unassign_constraint", self.file, product=element, constraint=constraint) assert ifcopenshell.util.constraint.get_constrained_elements(element) == set() assert len(self.file.by_type("IfcRelAssociatesConstraint")) == 0 + + @deprecation_check + def test_assign_a_declaration(self): + def get_declared_definitions(project: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]: + definitions = set() + for declares in project.Declares: + definitions.update(declares.RelatedDefinitions) + return definitions + + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + ifcopenshell.api.run( + "project.assign_declaration", + self.file, + definition=element_type, + relating_context=library, + ) + assert get_declared_definitions(library) == {element_type} + assert len(self.file.by_type("IfcRelDeclares")) == 1 From ac29e152bbbdcd71032c64520241a0751de0332e Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 14:58:32 +0500 Subject: [PATCH 24/62] project.unassign_declaration - support batching #4474 --- .../blenderbim/bim/module/project/operator.py | 2 +- .../ifcopenshell/api/__init__.py | 3 + .../api/project/unassign_declaration.py | 44 +++++----- .../api/sequence/assign_workplan.py | 2 +- .../ifcopenshell/api/sequence/remove_task.py | 2 +- .../api/sequence/remove_work_calendar.py | 2 +- .../api/sequence/remove_work_plan.py | 2 +- .../api/sequence/remove_work_schedule.py | 2 +- .../api/project/test_unassign_declaration.py | 82 +++++++++++++++++++ src/ifcopenshell-python/test/api/test_api.py | 22 +++++ 10 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 src/ifcopenshell-python/test/api/project/test_unassign_declaration.py diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 68c562c1c0..11b817c77f 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -338,7 +338,7 @@ class UnassignLibraryDeclaration(bpy.types.Operator): ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.file.by_id(self.definition), + definitions=[self.file.by_id(self.definition)], relating_context=self.file.by_type("IfcProjectLibrary")[0], ) element_name = self.props.active_library_element diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 1d21eaf40f..4249ed96aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -120,6 +120,9 @@ ARGUMENTS_DEPRECATION = { "project.assign_declaration": partial( batching_argument_deprecation, prev_argument="definition", new_argument="definitions" ), + "project.unassign_declaration": partial( + batching_argument_deprecation, prev_argument="definition", new_argument="definitions" + ), } diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py index 7e93f558fc..47c5194bdd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -22,13 +22,19 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, definition=None, relating_context=None): - """Unassigns an object to a project or project library + def __init__( + self, + file: ifcopenshell.file, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, + ): + """Unassigns a list of objects from a project or project library Typically used to remove an asset from a project library. - :param definition: The object you want to undeclare. Typically an asset. - :type definition: ifcopenshell.entity_instance.entity_instance + :param definitions: The list of objects you want to undeclare. + Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance.entity_instance] :param relating_context: The IfcProject, or more commonly the IfcProjectLibrary that you want the object to no longer be part of. :type relating_context: ifcopenshell.entity_instance.entity_instance @@ -49,25 +55,25 @@ class Usecase: ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) # Remove the library from our project - ifcopenshell.api.run("project.unassign_declaration", library, definition=context, relating_context=root) + ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root) """ self.file = file self.settings = { - "definition": definition, + "definitions": definitions, "relating_context": relating_context, } def execute(self): - if not self.settings["definition"].HasContext: - return - rel = self.settings["definition"].HasContext[0] - related_definitions = set(rel.RelatedDefinitions) or set() - related_definitions.remove(self.settings["definition"]) - if len(related_definitions): - rel.RelatedDefinitions = list(related_definitions) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + definitions = set(self.settings["definitions"]) + rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))} + + for rel in rels: + related_definitions = set(rel.RelatedDefinitions) - definitions + if related_definitions: + rel.RelatedDefinitions = list(related_definitions) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + else: + history = rel.OwnerHistory + self.file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(self.file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index a9e6f000eb..b3573db5d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -57,7 +57,7 @@ class Usecase: ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.settings["work_schedule"], + definitions=[self.settings["work_schedule"]], relating_context=self.file.by_type("IfcContext")[0], ) rel_aggregates = ifcopenshell.api.run( diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py index 4c0ee504f4..870efa1876 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py @@ -62,7 +62,7 @@ class Usecase: ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.settings["task"], + definitions=[self.settings["task"]], relating_context=self.file.by_type("IfcContext")[0], ) if self.settings["task"].TaskTime: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index a630c3f42e..242165c201 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -50,7 +50,7 @@ class Usecase: ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.settings["work_calendar"], + definitions=[self.settings["work_calendar"]], relating_context=self.file.by_type("IfcContext")[0], ) if self.settings["work_calendar"].Controls: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py index cb8c638799..905a7b9b8a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py @@ -50,7 +50,7 @@ class Usecase: ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.settings["work_plan"], + definitions=[self.settings["work_plan"]], relating_context=self.file.by_type("IfcContext")[0], ) history = self.settings["work_plan"].OwnerHistory diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py index 935c9f1832..b8bbcd4616 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py @@ -54,7 +54,7 @@ class Usecase: ifcopenshell.api.run( "project.unassign_declaration", self.file, - definition=self.settings["work_schedule"], + definitions=[self.settings["work_schedule"]], relating_context=self.file.by_type("IfcContext")[0], ) if self.settings["work_schedule"].Declares: diff --git a/src/ifcopenshell-python/test/api/project/test_unassign_declaration.py b/src/ifcopenshell-python/test/api/project/test_unassign_declaration.py new file mode 100644 index 0000000000..60dd46f7ad --- /dev/null +++ b/src/ifcopenshell-python/test/api/project/test_unassign_declaration.py @@ -0,0 +1,82 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api +from typing import Union + + +# NOTE: supported only in IFC4+ +class TestUnassignDeclaration(test.bootstrap.IFC4): + def get_context(self, definition: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: + rel = next(iter(definition.HasContext), None) + if rel is not None: + return rel.RelatingContext + + def test_unassigning_a_definition(self): + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type, element_type2], relating_context=library + ) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definitions=[element_type, element_type2], + relating_context=library, + ) + assert self.get_context(element_type) == None + assert len(self.file.by_type("IfcRelDeclares")) == 0 + + def test_doing_nothing_if_there_was_no_declaration(self): + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definitions=[element_type, element_type2], + relating_context=library, + ) + assert self.get_context(element_type) == None + assert self.get_context(element_type2) == None + + def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self): + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + element_type1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + element_type3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type1], relating_context=library + ) + rel = self.file.by_type("IfcRelDeclares")[0] + + ifcopenshell.api.run( + "project.assign_declaration", + self.file, + definitions=[element_type2, element_type3], + relating_context=library, + ) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definitions=[element_type1, element_type2], + relating_context=library, + ) + assert rel.RelatedDefinitions == (element_type3,) diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index 2f4db8786f..00d33b8b8a 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -23,6 +23,7 @@ import ifcopenshell.util.constraint import ifcopenshell.util.element import ifcopenshell.util.system from datetime import datetime +from typing import Union def deprecation_check(test): @@ -326,3 +327,24 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): ) assert get_declared_definitions(library) == {element_type} assert len(self.file.by_type("IfcRelDeclares")) == 1 + + @deprecation_check + def test_unassigning_a_definition(self): + def get_context(definition: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: + rel = next(iter(definition.HasContext), None) + if rel is not None: + return rel.RelatingContext + + library = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectLibrary") + element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + ifcopenshell.api.run( + "project.assign_declaration", self.file, definitions=[element_type], relating_context=library + ) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definition=element_type, + relating_context=library, + ) + assert get_context(element_type) == None + assert len(self.file.by_type("IfcRelDeclares")) == 0 From b1706f2eac09b3266ff9d8296a17889854d9980a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 15:42:56 +0500 Subject: [PATCH 25/62] fix error launching type manager in empty project --- src/blenderbim/blenderbim/bim/module/model/ui.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 9a174f11ff..7d7e8b679f 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -56,8 +56,11 @@ class LaunchTypeManager(bpy.types.Operator): ifc_class = props.ifc_class or AuthoringData.data["ifc_element_type"] else: ifc_class = AuthoringData.data["ifc_element_type"] - props.type_class = ifc_class - bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9) + + # will be None if project has no types + if ifc_class is not None: + props.type_class = ifc_class + bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9) return context.window_manager.invoke_popup(self, width=550) def draw(self, context): From a67108e6c193680e4a339a1805132881ab11062b Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 17:36:14 +0500 Subject: [PATCH 26/62] Reload current shading style after editing it #4567 Examples: 1) active shading type = EXTERNAL, now after changing it's SHADING attributes and accepting the changes, it will reload EXTERNAL shading back since it's the one that's active. 2) active shading type = SHADING, after adding EXTERNAL shader will reload SHADING style to the material. --- .../blenderbim/bim/module/style/operator.py | 25 ++++++++++++-- .../blenderbim/bim/module/style/prop.py | 34 ++++++++++++------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/style/operator.py b/src/blenderbim/blenderbim/bim/module/style/operator.py index 8133f16a5a..466a8ce3f6 100644 --- a/src/blenderbim/blenderbim/bim/module/style/operator.py +++ b/src/blenderbim/blenderbim/bim/module/style/operator.py @@ -23,6 +23,7 @@ import blenderbim.bim.handler import blenderbim.tool as tool import blenderbim.core.style as core import ifcopenshell.util.representation +from blenderbim.bim.module.style.prop import switch_shading from pathlib import Path from mathutils import Vector @@ -126,6 +127,10 @@ class DisableEditingStyle(bpy.types.Operator, tool.Ifc.Operator): tool.Style.reload_material_from_ifc(material) props.is_editing_style = 0 + # restore selected style type + material = tool.Ifc.get_object(style) + material.BIMStyleProperties.active_style_type = material.BIMStyleProperties.active_style_type + class EditStyle(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_style" @@ -332,7 +337,7 @@ class ActivateExternalStyle(bpy.types.Operator, tool.Ifc.Operator): if style_path.suffix != ".blend": self.report( {"ERROR"}, - f"Error loading external style for \"{material.name}\" - only Blender external styles are supported", + f'Error loading external style for "{material.name}" - only Blender external styles are supported', ) return {"CANCELLED"} @@ -587,7 +592,8 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): props.is_editing_class = self.ifc_class tool.Style.set_surface_style_props() - surface_style = tool.Style.get_style_elements(style).get(self.ifc_class, None) + style_elements = tool.Style.get_style_elements(style) + surface_style = style_elements.get(self.ifc_class, None) attributes = tool.Style.get_style_ui_props_attributes(self.ifc_class) # lighting style require special handling since Attribute doesn't support colors @@ -607,6 +613,17 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): attributes.clear() blenderbim.bim.helper.import_attributes2(surface_style or self.ifc_class, attributes, callback) + material = tool.Ifc.get_object(style) + active_style_type = material.BIMStyleProperties.active_style_type + if self.ifc_class == "IfcExternallyDefinedSurfaceStyle" and active_style_type != "External": + if tool.Style.has_blender_external_style(style_elements): + switch_shading(material, "External") + elif ( + self.ifc_class in ("IfcSurfaceStyleShading", "IfcSurfaceStyleRendering", "IfcSurfaceStyleWithTextures") + and active_style_type != "Shading" + ): + switch_shading(material, "Shading") + class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_surface_style" @@ -631,6 +648,10 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): self.props.is_editing_style = 0 core.load_styles(tool.Style, style_type=self.props.style_type) + # restore selected style type + material = tool.Ifc.get_object(self.style) + material.BIMStyleProperties.active_style_type = material.BIMStyleProperties.active_style_type + def edit_existing_style(self): material = tool.Ifc.get_object(self.style) if self.surface_style.is_a() == "IfcSurfaceStyleShading": diff --git a/src/blenderbim/blenderbim/bim/module/style/prop.py b/src/blenderbim/blenderbim/bim/module/style/prop.py index e195c1472a..af06b57615 100644 --- a/src/blenderbim/blenderbim/bim/module/style/prop.py +++ b/src/blenderbim/blenderbim/bim/module/style/prop.py @@ -33,6 +33,8 @@ from bpy.props import ( ) import gettext +from typing import Literal + _ = gettext.gettext @@ -251,19 +253,15 @@ class BIMStylesProperties(PropertyGroup): ) -def update_shading_style(self, context): - blender_material = self.id_data - style_elements = tool.Style.get_style_elements(blender_material) - if self.active_style_type == "External": - if tool.Style.has_blender_external_style(style_elements): - try: - bpy.ops.bim.activate_external_style(material_name=blender_material.name) - except RuntimeError as error: - if str(error).startswith("Error: Error loading external style for "): - return - raise error - - elif self.active_style_type == "Shading": +def switch_shading(blender_material: bpy.types.Material, style_type: Literal["External", "Shading"]) -> None: + if style_type == "External": + try: + bpy.ops.bim.activate_external_style(material_name=blender_material.name) + except RuntimeError as error: + if str(error).startswith("Error: Error loading external style for "): + return + raise error + elif style_type == "Shading": style_elements = tool.Style.get_style_elements(blender_material) rendering_style = None texture_style = None @@ -279,6 +277,16 @@ def update_shading_style(self, context): if rendering_style and texture_style: tool.Loader.create_surface_style_with_textures(blender_material, rendering_style, texture_style) + + +def update_shading_style(self, context): + blender_material = self.id_data + style_elements = tool.Style.get_style_elements(blender_material) + if self.active_style_type == "External": + if tool.Style.has_blender_external_style(style_elements): + switch_shading(blender_material, self.active_style_type) + elif self.active_style_type == "Shading": + switch_shading(blender_material, self.active_style_type) tool.Style.record_shading(blender_material) From 9e5c3ff6752a05fcf45e3c3ed9e3265110e41224 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 17:38:04 +0500 Subject: [PATCH 27/62] fix bug appending blender material when style.Location isn't .blend it wasn't considering that .Location could be either None or not a .blend file and was failing in those cases --- src/blenderbim/blenderbim/bim/module/style/operator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/style/operator.py b/src/blenderbim/blenderbim/bim/module/style/operator.py index 466a8ce3f6..600554ae93 100644 --- a/src/blenderbim/blenderbim/bim/module/style/operator.py +++ b/src/blenderbim/blenderbim/bim/module/style/operator.py @@ -251,14 +251,15 @@ class BrowseExternalStyle(bpy.types.Operator): ) def invoke(self, context, event): - external_style = None + style_elements = None if self.active_surface_style_id: style = tool.Ifc.get().by_id(self.active_surface_style_id) - external_style = tool.Style.get_style_elements(style).get("IfcExternallyDefinedSurfaceStyle", None) + style_elements = tool.Style.get_style_elements(style) # automatically select previously selected external style in file browser # if it exists in the file - if external_style and self.filepath == "": + if style_elements and self.filepath == "" and tool.Style.has_blender_external_style(style_elements): + external_style = style_elements["IfcExternallyDefinedSurfaceStyle"] style_path = Path(tool.Ifc.resolve_uri(external_style.Location)) self.directory = str(style_path.parent) self.filepath = str(style_path) From 9f2df7fb9d4b0bc77783c24e4c95d9f487021ba6 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 3 May 2024 17:55:30 +0500 Subject: [PATCH 28/62] preview external shading style as material is selected from other .blend file previously it required to save external style attributes to preview the changes, now it's couple clicks saved if you want to preview different blender materials for your styles --- .../blenderbim/bim/module/style/operator.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/style/operator.py b/src/blenderbim/blenderbim/bim/module/style/operator.py index 600554ae93..e59e60da74 100644 --- a/src/blenderbim/blenderbim/bim/module/style/operator.py +++ b/src/blenderbim/blenderbim/bim/module/style/operator.py @@ -316,6 +316,9 @@ class BrowseExternalStyle(bpy.types.Operator): attributes["Location"].string_value = filepath attributes["Identification"].string_value = f"{self.data_block_type}/{self.data_block}" attributes["Name"].string_value = self.data_block + + style = tool.Ifc.get().by_id(self.active_surface_style_id) + bpy.ops.bim.activate_external_style(material_name=tool.Ifc.get_object(style).name) return {"FINISHED"} @@ -331,9 +334,18 @@ class ActivateExternalStyle(bpy.types.Operator, tool.Ifc.Operator): material = context.active_object.active_material else: material = bpy.data.materials[self.material_name] - external_style = tool.Style.get_style_elements(material)["IfcExternallyDefinedSurfaceStyle"] - data_block_type, data_block = external_style.Identification.split("/") - style_path = Path(tool.Ifc.resolve_uri(external_style.Location)) + + props = context.scene.BIMStylesProperties + if props.is_editing: + location = props.external_style_attributes["Location"].string_value + identification = props.external_style_attributes["Identification"].string_value + else: + external_style = tool.Style.get_style_elements(material)["IfcExternallyDefinedSurfaceStyle"] + location = external_style.Location + identification = external_style.Identification + + data_block_type, data_block = identification.split("/") + style_path = Path(tool.Ifc.resolve_uri(location)) if style_path.suffix != ".blend": self.report( From ab696b980a00fe57fdef753a1084d790e9f2b757 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 5 May 2024 16:26:41 +1000 Subject: [PATCH 29/62] See #2693. Expose static functions of IfcOpenShell API. --- .../ifcopenshell/api/__init__.py | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 4249ed96aa..805811744e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -20,6 +20,7 @@ import json import numpy +import pkgutil import importlib import ifcopenshell import ifcopenshell.api @@ -126,15 +127,26 @@ ARGUMENTS_DEPRECATION = { } -CACHED_USECASE_CLASSES = dict() +CACHED_USECASE_CLASSES = {} +CACHED_USECASES = {} def run( usecase_path: str, ifc_file: Optional[ifcopenshell.file] = None, - should_run_listeners=True, + should_run_listeners: bool = True, **settings: Any, ) -> Any: + usecase_function = CACHED_USECASES.get(usecase_path) + if not usecase_function: + importlib.import_module(f"ifcopenshell.api.{usecase_path}") + module, usecase = usecase_path.split(".") + usecase_function = getattr(getattr(ifcopenshell.api, module), usecase) + CACHED_USECASES[usecase_path] = usecase_function + if ifc_file: + return usecase_function(ifc_file, should_run_listeners=should_run_listeners, **settings) + return usecase_function(should_run_listeners=should_run_listeners, **settings) + if should_run_listeners: for listener in pre_listeners.get(usecase_path, {}).values(): listener(usecase_path, ifc_file, settings) @@ -281,3 +293,69 @@ def extract_docs(module, usecase): node_data["description"] = description.strip() node_data["inputs"] = inputs return node_data + + +def _wrap_api(init_globals, file, package): + """API endpoints are implemented as Usecase classes. This wraps the classes as functions. + + Calling classes is syntactically awkward. For example, + ifcopenshell.api.root.create_entity.Usecase(f).execute(). + It is more elegant to call it using ifcopenshell.api.root.create_entity(f). + + Calling _wrap_api from an API package's __init__.py will generate these + wrapper functions at runtime. + """ + import pkgutil + import importlib + import inspect + from pathlib import Path + + def _create_function(module_name, Usecase): + """Create a function that wraps the Usecase class's execute method.""" + usecase_path = ".".join(Usecase.__module__.split(".")[-2:]) + + def wrapper(*args, should_run_listeners: bool = True, **settings): + ifc_file = args[0] if args else None + if should_run_listeners: + for listener in pre_listeners.get(usecase_path, {}).values(): + listener(usecase_path, ifc_file, settings) + + try: + usecase = Usecase(*args, **settings) + except TypeError as e: + msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(Usecase.__init__)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation." + raise TypeError(msg) from e + + result = usecase.execute() + + if should_run_listeners: + for listener in post_listeners.get(usecase_path, {}).values(): + listener(usecase_path, ifc_file, settings) + + return result + + wrapper.__signature__ = inspect.signature(Usecase.__init__) + wrapper.__doc__ = Usecase.__init__.__doc__ + wrapper.__name__ = module_name + return wrapper + + for finder, name, ispkg in pkgutil.iter_modules([Path(file).parent]): + try: + module = importlib.import_module(f".{name}", package) + except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: {package}.{name} - {e}") + continue + usecase_cls = getattr(module, "Usecase", None) + if usecase_cls: + func = _create_function(name, usecase_cls) + init_globals[name] = func + + +# Expose all submodules. This means that the user can just type `import ifcopenshell.api`. +for loader, module_name, is_pkg in pkgutil.iter_modules(__path__, __name__ + "."): + module = importlib.import_module(module_name) + + # Check if it's a direct child (only one level deep) + if module_name.count(".") == __name__.count(".") + 1: + # Generate wrapper functions for each usecase + _wrap_api(vars(module), module.__file__, module.__name__) From 5f2d451c5e9e7d5e3da67e18970569cd9f91c9a3 Mon Sep 17 00:00:00 2001 From: ppaawweeuu <61344631+ppaawweeuu@users.noreply.github.com> Date: Sun, 5 May 2024 15:40:32 +0200 Subject: [PATCH 30/62] Update selector_syntax.rst (#4618) info about 'Must contain..' and 'Must not contain..' comparisons added --- .../docs/ifcopenshell-python/selector_syntax.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst index 3ae94c26a8..a21594ace5 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/selector_syntax.rst @@ -116,6 +116,8 @@ the following comparison checks: "``>=``", "Must be greater than or equal to the value." "``<``", "Must be less than the value." "``<=``", "Must be less than or equal to the value." + "``*=``", "Must contain the value." + "``!*=``", "Must not contain the value." When you specify a ``{{pset}}``, ``{{prop}}``, or ``{{value}}``, there are three ways you can do so: From d694d8fcc2da394bfb645456af752950ed806d86 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Sun, 5 May 2024 13:16:30 -0500 Subject: [PATCH 31/62] fix #4610: bim.select_type() from type launcher throws error --- .../blenderbim/bim/module/model/ui.py | 1 + .../blenderbim/bim/module/type/operator.py | 49 +++++++++---------- .../blenderbim/bim/module/type/ui.py | 2 +- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/ui.py b/src/blenderbim/blenderbim/bim/module/model/ui.py index 7d7e8b679f..0ad66da688 100644 --- a/src/blenderbim/blenderbim/bim/module/model/ui.py +++ b/src/blenderbim/blenderbim/bim/module/model/ui.py @@ -137,6 +137,7 @@ class LaunchTypeManager(bpy.types.Operator): op = row.operator("bim.rename_type", icon="GREASEPENCIL", text="") op.element = relating_type["id"] op = row.operator("bim.select_type", icon="OBJECT_DATA", text="") + op.relating_type = relating_type["id"] op = row.operator("bim.duplicate_type", icon="DUPLICATE", text="") op.element = relating_type["id"] op = row.operator("bim.remove_type", icon="X", text="") diff --git a/src/blenderbim/blenderbim/bim/module/type/operator.py b/src/blenderbim/blenderbim/bim/module/type/operator.py index 66d8682965..f5f08c35b3 100644 --- a/src/blenderbim/blenderbim/bim/module/type/operator.py +++ b/src/blenderbim/blenderbim/bim/module/type/operator.py @@ -146,39 +146,36 @@ class SelectType(bpy.types.Operator): relating_type: bpy.props.IntProperty() def execute(self, context): - selected_objs = context.selected_objects - active_obj = context.active_object - selected_objs.append(active_obj) #update selected_objs so the active_obj is at the end of the list + + if self.relating_type: #if operator button sends a relating_type, the iterator only selects this one type + element = tool.Ifc.get().by_id(self.relating_type) + obj = tool.Ifc.get_object(element) + selected_objs = [obj] + else: #else, the iterator selects all the types of all the selected objects + selected_objs = context.selected_objects + active_obj = context.active_object + selected_objs.append(active_obj) #update selected_objs so the active_obj is at the end of the list + last_relating_type_obj = None types_collection = bpy.data.collections.get("Types") - for obj in types_collection.objects: - obj.hide_set(True) + context.view_layer.layer_collection.children['IfcProject/My Project'].children["Types"].hide_viewport = False + for type_obj in types_collection.objects: + type_obj.hide_set(True) for obj in selected_objs: element = tool.Ifc.get_entity(obj) relating_type = ifcopenshell.util.element.get_type(element) - relating_type_obj = tool.Ifc.get_object(relating_type) - obj.select_set(False) - if relating_type_obj: - if relating_type_obj.hide_get(): - relating_type_obj.hide_set(False) - relating_type_obj.select_set(True) - last_relating_type_obj = relating_type_obj + if relating_type: + relating_type_obj = tool.Ifc.get_object(relating_type) + if relating_type_obj: + if relating_type_obj.hide_get(): + relating_type_obj.hide_set(False) + relating_type_obj.select_set(True) + last_relating_type_obj = relating_type_obj + if not element.is_a("IfcTypeObject"): + obj.select_set(False) - context.view_layer.objects.active = last_relating_type_obj #make the active_obj's type the active object + context.view_layer.objects.active = last_relating_type_obj #makes the active_obj's type the active object - # if relating_type_obj: - # try: - # tool.Blender.select_and_activate_single_object(context, relating_type_obj) - # except: - # self.report({"INFO"}, "Type object is hidden.") - # # IfcTypeProducts are only used for annotations and not part of the model interface. - # if element.is_a() != "IfcTypeProduct": - # try: - # context.scene.BIMModelProperties.ifc_class = element.is_a() - # context.scene.BIMModelProperties.relating_type_id = str(relating_type) - # except: - # # Potentially our BIM Tool is filtered to a specific element. - # pass return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/type/ui.py b/src/blenderbim/blenderbim/bim/module/type/ui.py index 5fa45c9ca6..6275ae52ef 100644 --- a/src/blenderbim/blenderbim/bim/module/type/ui.py +++ b/src/blenderbim/blenderbim/bim/module/type/ui.py @@ -88,7 +88,7 @@ class BIM_PT_type(Panel): if TypeData.data["relating_type"]: row.label(text=TypeData.data["relating_type"]["name"]) op = row.operator("bim.select_type", icon="OBJECT_DATA", text="") - op.relating_type = TypeData.data["relating_type"]["id"] + op.relating_type = 0 #will only select the relating types of only the selected objects row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="") row.operator("bim.enable_editing_type", icon="GREASEPENCIL", text="") row.operator("bim.unassign_type", icon="X", text="") From a213ab6760eb2d21c5cf98dafce90a5519707312 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 6 May 2024 08:39:41 +1000 Subject: [PATCH 32/62] Fix docstrings to reference datatype as it would be used for users --- .../ifcopenshell/api/aggregate/assign_object.py | 6 +++--- .../ifcopenshell/api/aggregate/unassign_object.py | 2 +- .../ifcopenshell/api/attribute/edit_attributes.py | 2 +- .../api/boundary/assign_connection_geometry.py | 2 +- .../ifcopenshell/api/boundary/copy_boundary.py | 2 +- .../ifcopenshell/api/boundary/edit_attributes.py | 10 +++++----- .../ifcopenshell/api/boundary/remove_boundary.py | 2 +- .../api/classification/add_classification.py | 4 ++-- .../ifcopenshell/api/classification/add_reference.py | 8 ++++---- .../api/classification/edit_classification.py | 2 +- .../ifcopenshell/api/classification/edit_reference.py | 2 +- .../api/classification/remove_classification.py | 2 +- .../api/classification/remove_reference.py | 4 ++-- .../ifcopenshell/api/constraint/add_metric.py | 4 ++-- .../ifcopenshell/api/constraint/add_objective.py | 2 +- .../ifcopenshell/api/constraint/assign_constraint.py | 6 +++--- .../ifcopenshell/api/constraint/edit_metric.py | 2 +- .../ifcopenshell/api/constraint/edit_objective.py | 2 +- .../ifcopenshell/api/constraint/remove_constraint.py | 2 +- .../ifcopenshell/api/constraint/remove_metric.py | 2 +- .../ifcopenshell/api/constraint/unassign_constraint.py | 4 ++-- .../ifcopenshell/api/context/add_context.py | 4 ++-- .../ifcopenshell/api/context/edit_context.py | 2 +- .../ifcopenshell/api/context/remove_context.py | 2 +- .../ifcopenshell/api/control/assign_control.py | 6 +++--- .../ifcopenshell/api/control/unassign_control.py | 6 +++--- .../ifcopenshell/api/cost/add_cost_item.py | 6 +++--- .../ifcopenshell/api/cost/add_cost_item_quantity.py | 4 ++-- .../ifcopenshell/api/cost/add_cost_schedule.py | 2 +- .../ifcopenshell/api/cost/add_cost_value.py | 4 ++-- .../ifcopenshell/api/cost/assign_cost_item_quantity.py | 4 ++-- .../ifcopenshell/api/cost/assign_cost_value.py | 4 ++-- .../ifcopenshell/api/cost/copy_cost_item.py | 4 ++-- .../ifcopenshell/api/cost/copy_cost_item_values.py | 4 ++-- .../ifcopenshell/api/cost/edit_cost_item.py | 2 +- .../ifcopenshell/api/cost/edit_cost_item_quantity.py | 2 +- .../ifcopenshell/api/cost/edit_cost_schedule.py | 2 +- .../ifcopenshell/api/cost/edit_cost_value.py | 2 +- .../ifcopenshell/api/cost/edit_cost_value_formula.py | 2 +- .../ifcopenshell/api/cost/remove_cost_item.py | 2 +- .../ifcopenshell/api/cost/remove_cost_item_quantity.py | 4 ++-- .../ifcopenshell/api/cost/remove_cost_schedule.py | 2 +- .../ifcopenshell/api/cost/remove_cost_value.py | 4 ++-- .../api/cost/unassign_cost_item_quantity.py | 4 ++-- .../ifcopenshell/api/document/add_information.py | 4 ++-- .../ifcopenshell/api/document/add_reference.py | 4 ++-- .../ifcopenshell/api/document/assign_document.py | 6 +++--- .../ifcopenshell/api/document/edit_information.py | 2 +- .../ifcopenshell/api/document/edit_reference.py | 2 +- .../ifcopenshell/api/document/remove_information.py | 2 +- .../ifcopenshell/api/document/remove_reference.py | 2 +- .../ifcopenshell/api/document/unassign_document.py | 4 ++-- .../ifcopenshell/api/drawing/assign_product.py | 6 +++--- .../ifcopenshell/api/drawing/edit_text_literal.py | 2 +- .../ifcopenshell/api/drawing/unassign_product.py | 6 +++--- .../api/geometry/add_axis_representation.py | 4 ++-- .../ifcopenshell/api/grid/create_axis_curve.py | 2 +- .../ifcopenshell/api/grid/create_grid_axis.py | 4 ++-- .../ifcopenshell/api/grid/remove_grid_axis.py | 2 +- .../ifcopenshell/api/group/add_group.py | 2 +- .../ifcopenshell/api/group/assign_group.py | 6 +++--- .../ifcopenshell/api/group/edit_group.py | 2 +- .../ifcopenshell/api/group/remove_group.py | 2 +- .../ifcopenshell/api/group/unassign_group.py | 4 ++-- .../ifcopenshell/api/group/update_group_products.py | 6 +++--- .../ifcopenshell/api/layer/add_layer.py | 2 +- .../ifcopenshell/api/layer/assign_layer.py | 4 ++-- .../ifcopenshell/api/layer/edit_layer.py | 2 +- .../ifcopenshell/api/layer/remove_layer.py | 2 +- .../ifcopenshell/api/layer/unassign_layer.py | 4 ++-- .../ifcopenshell/api/library/add_library.py | 2 +- .../ifcopenshell/api/library/add_reference.py | 4 ++-- .../ifcopenshell/api/library/assign_reference.py | 6 +++--- .../ifcopenshell/api/library/edit_library.py | 2 +- .../ifcopenshell/api/library/edit_reference.py | 2 +- .../ifcopenshell/api/library/remove_library.py | 2 +- .../ifcopenshell/api/library/remove_reference.py | 2 +- .../ifcopenshell/api/library/unassign_reference.py | 4 ++-- .../ifcopenshell/api/material/add_constituent.py | 6 +++--- .../ifcopenshell/api/material/add_layer.py | 6 +++--- .../ifcopenshell/api/material/add_list_item.py | 4 ++-- .../ifcopenshell/api/material/add_material.py | 2 +- .../ifcopenshell/api/material/add_material_set.py | 2 +- .../ifcopenshell/api/material/add_profile.py | 8 ++++---- .../ifcopenshell/api/material/assign_material.py | 8 ++++---- .../ifcopenshell/api/material/assign_profile.py | 4 ++-- .../ifcopenshell/api/material/copy_material.py | 4 ++-- .../api/material/edit_assigned_material.py | 2 +- .../ifcopenshell/api/material/edit_constituent.py | 4 ++-- .../ifcopenshell/api/material/edit_layer.py | 4 ++-- .../ifcopenshell/api/material/edit_layer_usage.py | 2 +- .../ifcopenshell/api/material/edit_profile.py | 6 +++--- .../ifcopenshell/api/material/edit_profile_usage.py | 2 +- .../ifcopenshell/api/material/remove_constituent.py | 2 +- .../ifcopenshell/api/material/remove_layer.py | 2 +- .../ifcopenshell/api/material/remove_list_item.py | 2 +- .../ifcopenshell/api/material/remove_material.py | 2 +- .../ifcopenshell/api/material/remove_material_set.py | 2 +- .../ifcopenshell/api/material/remove_profile.py | 2 +- .../ifcopenshell/api/material/reorder_set_item.py | 2 +- .../ifcopenshell/api/material/unassign_material.py | 2 +- .../ifcopenshell/api/nest/assign_object.py | 6 +++--- .../ifcopenshell/api/nest/unassign_object.py | 2 +- .../ifcopenshell/api/owner/add_actor.py | 4 ++-- .../ifcopenshell/api/owner/add_address.py | 4 ++-- .../ifcopenshell/api/owner/add_application.py | 2 +- .../ifcopenshell/api/owner/add_organisation.py | 2 +- .../ifcopenshell/api/owner/add_person.py | 2 +- .../api/owner/add_person_and_organisation.py | 6 +++--- .../ifcopenshell/api/owner/add_role.py | 4 ++-- .../ifcopenshell/api/owner/assign_actor.py | 6 +++--- .../ifcopenshell/api/owner/create_owner_history.py | 2 +- .../ifcopenshell/api/owner/edit_actor.py | 2 +- .../ifcopenshell/api/owner/edit_address.py | 2 +- .../ifcopenshell/api/owner/edit_organisation.py | 2 +- .../ifcopenshell/api/owner/edit_person.py | 2 +- .../ifcopenshell/api/owner/edit_role.py | 2 +- .../ifcopenshell/api/owner/remove_actor.py | 2 +- .../ifcopenshell/api/owner/remove_address.py | 2 +- .../ifcopenshell/api/owner/remove_application.py | 2 +- .../ifcopenshell/api/owner/remove_organisation.py | 2 +- .../ifcopenshell/api/owner/remove_person.py | 2 +- .../api/owner/remove_person_and_organisation.py | 2 +- .../ifcopenshell/api/owner/remove_role.py | 2 +- .../ifcopenshell/api/owner/settings.py | 8 ++++---- .../ifcopenshell/api/owner/unassign_actor.py | 6 +++--- .../ifcopenshell/api/owner/update_owner_history.py | 4 ++-- .../ifcopenshell/api/profile/add_arbitrary_profile.py | 2 +- .../api/profile/add_arbitrary_profile_with_voids.py | 2 +- .../api/profile/add_parameterized_profile.py | 4 ++-- .../ifcopenshell/api/profile/edit_profile.py | 2 +- .../ifcopenshell/api/profile/remove_profile.py | 2 +- .../ifcopenshell/api/project/append_asset.py | 8 ++++---- .../ifcopenshell/api/project/assign_declaration.py | 6 +++--- .../ifcopenshell/api/project/create_file.py | 2 +- .../ifcopenshell/api/project/unassign_declaration.py | 4 ++-- .../ifcopenshell/api/pset/add_pset.py | 4 ++-- .../ifcopenshell/api/pset/add_qto.py | 4 ++-- .../ifcopenshell/api/pset/edit_pset.py | 4 ++-- .../ifcopenshell/api/pset/edit_qto.py | 4 ++-- .../ifcopenshell/api/pset/remove_pset.py | 4 ++-- .../api/pset_template/add_prop_template.py | 4 ++-- .../api/pset_template/add_pset_template.py | 2 +- .../api/pset_template/edit_prop_template.py | 2 +- .../api/pset_template/edit_pset_template.py | 2 +- .../api/pset_template/remove_prop_template.py | 2 +- .../api/pset_template/remove_pset_template.py | 2 +- .../ifcopenshell/api/resource/add_resource.py | 4 ++-- .../ifcopenshell/api/resource/add_resource_quantity.py | 4 ++-- .../ifcopenshell/api/resource/add_resource_time.py | 4 ++-- .../ifcopenshell/api/resource/assign_resource.py | 6 +++--- .../api/resource/calculate_resource_work.py | 2 +- .../ifcopenshell/api/resource/edit_resource.py | 2 +- .../api/resource/edit_resource_quantity.py | 2 +- .../ifcopenshell/api/resource/edit_resource_time.py | 2 +- .../ifcopenshell/api/resource/unassign_resource.py | 6 +++--- .../ifcopenshell/api/root/copy_class.py | 4 ++-- .../ifcopenshell/api/root/create_entity.py | 2 +- .../ifcopenshell/api/root/reassign_class.py | 4 ++-- .../ifcopenshell/api/root/remove_product.py | 2 +- .../ifcopenshell/api/sequence/add_task.py | 6 +++--- .../ifcopenshell/api/sequence/add_task_time.py | 4 ++-- .../ifcopenshell/api/sequence/add_time_period.py | 4 ++-- .../ifcopenshell/api/sequence/add_work_calendar.py | 2 +- .../ifcopenshell/api/sequence/add_work_plan.py | 2 +- .../ifcopenshell/api/sequence/add_work_schedule.py | 4 ++-- .../ifcopenshell/api/sequence/add_work_time.py | 4 ++-- .../ifcopenshell/api/sequence/assign_lag_time.py | 4 ++-- .../ifcopenshell/api/sequence/assign_process.py | 6 +++--- .../ifcopenshell/api/sequence/assign_product.py | 6 +++--- .../api/sequence/assign_recurrence_pattern.py | 4 ++-- .../ifcopenshell/api/sequence/assign_sequence.py | 6 +++--- .../ifcopenshell/api/sequence/assign_workplan.py | 6 +++--- .../api/sequence/calculate_task_duration.py | 2 +- .../ifcopenshell/api/sequence/cascade_schedule.py | 2 +- .../ifcopenshell/api/sequence/create_baseline.py | 4 ++-- .../ifcopenshell/api/sequence/duplicate_task.py | 4 ++-- .../ifcopenshell/api/sequence/edit_lag_time.py | 2 +- .../api/sequence/edit_recurrence_pattern.py | 2 +- .../ifcopenshell/api/sequence/edit_sequence.py | 2 +- .../ifcopenshell/api/sequence/edit_task.py | 2 +- .../ifcopenshell/api/sequence/edit_task_time.py | 2 +- .../ifcopenshell/api/sequence/edit_work_calendar.py | 2 +- .../ifcopenshell/api/sequence/edit_work_plan.py | 2 +- .../ifcopenshell/api/sequence/edit_work_schedule.py | 2 +- .../ifcopenshell/api/sequence/edit_work_time.py | 2 +- .../ifcopenshell/api/sequence/get_related_products.py | 6 +++--- .../ifcopenshell/api/sequence/recalculate_schedule.py | 2 +- .../ifcopenshell/api/sequence/remove_task.py | 2 +- .../ifcopenshell/api/sequence/remove_time_period.py | 2 +- .../ifcopenshell/api/sequence/remove_work_calendar.py | 2 +- .../ifcopenshell/api/sequence/remove_work_plan.py | 2 +- .../ifcopenshell/api/sequence/remove_work_schedule.py | 2 +- .../ifcopenshell/api/sequence/remove_work_time.py | 2 +- .../ifcopenshell/api/sequence/unassign_lag_time.py | 2 +- .../ifcopenshell/api/sequence/unassign_process.py | 4 ++-- .../ifcopenshell/api/sequence/unassign_product.py | 4 ++-- .../api/sequence/unassign_recurrence_pattern.py | 2 +- .../ifcopenshell/api/sequence/unassign_sequence.py | 4 ++-- .../ifcopenshell/api/spatial/assign_container.py | 4 ++-- .../ifcopenshell/api/spatial/dereference_structure.py | 2 +- .../ifcopenshell/api/spatial/reference_structure.py | 4 ++-- .../ifcopenshell/api/spatial/unassign_container.py | 2 +- .../api/structural/add_structural_activity.py | 6 +++--- .../api/structural/add_structural_analysis_model.py | 2 +- .../structural/add_structural_boundary_condition.py | 4 ++-- .../ifcopenshell/api/structural/add_structural_load.py | 2 +- .../api/structural/add_structural_load_case.py | 2 +- .../api/structural/add_structural_load_group.py | 2 +- .../api/structural/add_structural_member_connection.py | 6 +++--- .../api/structural/assign_structural_analysis_model.py | 6 +++--- .../api/structural/edit_structural_analysis_model.py | 2 +- .../structural/edit_structural_boundary_condition.py | 2 +- .../api/structural/edit_structural_connection_cs.py | 2 +- .../api/structural/edit_structural_item_axis.py | 2 +- .../api/structural/edit_structural_load.py | 2 +- .../api/structural/edit_structural_load_case.py | 2 +- .../api/structural/remove_structural_analysis_model.py | 2 +- .../structural/remove_structural_boundary_condition.py | 4 ++-- .../remove_structural_connection_condition.py | 2 +- .../api/structural/remove_structural_load.py | 2 +- .../api/structural/remove_structural_load_case.py | 2 +- .../api/structural/remove_structural_load_group.py | 2 +- .../structural/unassign_structural_analysis_model.py | 4 ++-- .../ifcopenshell/api/style/add_style.py | 2 +- .../ifcopenshell/api/style/add_surface_style.py | 4 ++-- .../ifcopenshell/api/style/add_surface_textures.py | 4 ++-- .../ifcopenshell/api/style/assign_material_style.py | 6 +++--- .../api/style/assign_representation_styles.py | 6 +++--- .../ifcopenshell/api/style/edit_presentation_style.py | 2 +- .../ifcopenshell/api/style/edit_surface_style.py | 2 +- .../ifcopenshell/api/style/remove_style.py | 2 +- .../api/style/remove_styled_representation.py | 2 +- .../ifcopenshell/api/style/remove_surface_style.py | 2 +- .../ifcopenshell/api/style/unassign_material_style.py | 6 +++--- .../api/style/unassign_representation_styles.py | 4 ++-- .../ifcopenshell/api/system/add_port.py | 4 ++-- .../ifcopenshell/api/system/add_system.py | 2 +- .../ifcopenshell/api/system/assign_flow_control.py | 6 +++--- .../ifcopenshell/api/system/assign_port.py | 6 +++--- .../ifcopenshell/api/system/assign_system.py | 6 +++--- .../ifcopenshell/api/system/connect_port.py | 6 +++--- .../ifcopenshell/api/system/disconnect_port.py | 2 +- .../ifcopenshell/api/system/edit_system.py | 2 +- .../ifcopenshell/api/system/remove_system.py | 2 +- .../ifcopenshell/api/system/unassign_flow_control.py | 6 +++--- .../ifcopenshell/api/system/unassign_port.py | 4 ++-- .../ifcopenshell/api/system/unassign_system.py | 4 ++-- .../ifcopenshell/api/type/assign_type.py | 6 +++--- .../ifcopenshell/api/type/get_related_objects.py | 6 +++--- .../ifcopenshell/api/type/map_type_representations.py | 4 ++-- .../ifcopenshell/api/type/unassign_type.py | 2 +- .../api/unit/add_context_dependent_unit.py | 2 +- .../ifcopenshell/api/unit/add_conversion_based_unit.py | 2 +- .../ifcopenshell/api/unit/add_monetary_unit.py | 2 +- .../ifcopenshell/api/unit/add_si_unit.py | 2 +- .../ifcopenshell/api/unit/assign_unit.py | 4 ++-- .../ifcopenshell/api/unit/edit_derived_unit.py | 2 +- .../ifcopenshell/api/unit/edit_monetary_unit.py | 2 +- .../ifcopenshell/api/unit/edit_named_unit.py | 2 +- .../ifcopenshell/api/unit/remove_unit.py | 2 +- .../ifcopenshell/api/unit/unassign_unit.py | 2 +- .../ifcopenshell/api/void/add_filling.py | 6 +++--- .../ifcopenshell/api/void/add_opening.py | 6 +++--- .../ifcopenshell/api/void/remove_filling.py | 2 +- .../ifcopenshell/api/void/remove_opening.py | 2 +- 266 files changed, 439 insertions(+), 439 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py index 58bf9daa13..3e9f866435 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py @@ -63,13 +63,13 @@ class Usecase: :param products: The list of parts of the aggregate, typically of IfcElement or IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :param relating_object: The whole of the aggregate, typically an IfcElement or IfcSpatialStructureElement subclass - :type relating_object: ifcopenshell.entity_instance.entity_instance + :type relating_object: ifcopenshell.entity_instance :return: The IfcRelAggregate relationship instance or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py index e4d0e35def..c766a4f019 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py @@ -39,7 +39,7 @@ class Usecase: :param products: The list of parts of the aggregate, typically of IfcElements or IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py index 42365ca802..1e2cd98bc5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py @@ -30,7 +30,7 @@ class Usecase: :param product: The product you want to edit. This may be any rooted IFC entity. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py index 205eba2ce7..8f23a60bb6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py @@ -31,7 +31,7 @@ class Usecase: :param rel_space_boundary: The space boundary relationship to assign the connection geometry to. - :type rel_space_boundary: ifcopenshell.entity_instance.entity_instance + :type rel_space_boundary: ifcopenshell.entity_instance :param outer_boundary: A list of 2D points representing an open polyline. The last point will connect to the first point. Each point is represented by an interable of 2 floats. The coordinates of diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py index af78438421..2f8b092c51 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py @@ -24,7 +24,7 @@ class Usecase: """Copies a space boundary :param boundary: The IfcRelSpaceBoundary you want to copy. - :type boundary: ifcopenshell.entity_instance.entity_instance + :type boundary: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py index a67b3fb2bb..4be540f7a2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py @@ -25,22 +25,22 @@ class Usecase: manual assignment of the space boundary attributes. :param entity: The IfcRelSpaceBoundary to modify - :type entity: ifcopenshell.entity_instance.entity_instance + :type entity: ifcopenshell.entity_instance :param relating_space: The IfcSpace or IfcExternalSpatialElement that the space boundary is related to. - :type relating_space: ifcopenshell.entity_instance.entity_instance + :type relating_space: ifcopenshell.entity_instance :param related_building_element: The IfcElement that defines the boundary, typically an IfcWall. - :type relating_space: ifcopenshell.entity_instance.entity_instance + :type relating_space: ifcopenshell.entity_instance :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if this is an inner boundary. This can apply to 1st and 2nd level boundaries. - :type parent_boundary: ifcopenshell.entity_instance.entity_instance, + :type parent_boundary: ifcopenshell.entity_instance, optional :param corresponding_boundary: The other IfcRelSpaceBoundary on the other side of the related element. The pair together represents a thermal boundary. This only applies to 2nd level boundaries. - :type corresponding_boundary: ifcopenshell.entity_instance.entity_instance, + :type corresponding_boundary: ifcopenshell.entity_instance, optional :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py index 2d77f74f06..6744da820c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py @@ -28,7 +28,7 @@ class Usecase: boundary and its connection geometry is removed. :param boundary: The IfcRelSpaceBoundary you want to remove. - :type boundary: ifcopenshell.entity_instance.entity_instance + :type boundary: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py index d59a4e9343..580c036a5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py @@ -59,9 +59,9 @@ class Usecase: classification library. The latter approach is preferred if you are using a commonly known system such as Uniclass, as this will ensure all metadata is added correctly. - :type classification: str,ifcopenshell.entity_instance.entity_instance + :type classification: str,ifcopenshell.entity_instance :return: The added IfcClassification element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index 9057f7a25f..db1bab41bf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -66,11 +66,11 @@ class Usecase: :param product: The list of IFC objects, properties, or resources you want to associate the classification reference to. - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :param reference: The classification reference entity taken from an IFC classification library. If you supply this parameter, you will use option 2. - :type reference: ifcopenshell.entity_instance.entity_instance, optional + :type reference: ifcopenshell.entity_instance, optional :param identification: If you choose option 1 and do not specify a reference, you may manually specify an identification code. The code is typically a short identifier and may have punctuation to separate @@ -82,7 +82,7 @@ class Usecase: :param classification: The IfcClassification entity in your IFC model (not the library, if you are doing option 2) that the reference is part of. - :type classification: ifcopenshell.entity_instance.entity_instance + :type classification: ifcopenshell.entity_instance :param is_lightweight: If you are doing option 2, choose whether or not to only add that particular reference (lighweight) or also add all of its parent references in the classification hierarchy (not @@ -98,7 +98,7 @@ class Usecase: :return: The newly added IfcClassificationReference or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py index bd590ddd38..9925c59f54 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py @@ -25,7 +25,7 @@ class Usecase: IfcClassification, consult the IFC documentation. :param classification: The IfcClassification entity you want to edit - :type classification: ifcopenshell.entity_instance.entity_instance + :type classification: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py index 4b47aebc71..4acf396adb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py @@ -25,7 +25,7 @@ class Usecase: IfcClassificationReference, consult the IFC documentation. :param reference: The IfcClassificationReference entity you want to edit - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py index 2cf46cb091..42a5dcacd0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -29,7 +29,7 @@ class Usecase: removed from a project. :param classification: The IfcClassification entity you want to remove - :type classification: ifcopenshell.entity_instance.entity_instance + :type classification: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py index d4e8d802a6..ea61fb002d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py @@ -35,10 +35,10 @@ class Usecase: :param reference: The IfcClassificationReference entity of the relationship you want to remove. - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param product: The list fo object entities of the relationship you want to remove. - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py index 2941eb8308..b84e2f3cc9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py @@ -29,9 +29,9 @@ class Usecase: to meet the objective of the constraint. :param objective: The IfcObjective that this metric is a benchmark of. - :type objective: ifcopenshell.entity_instance.entity_instance + :type objective: ifcopenshell.entity_instance :return: The newly created IfcMetric entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py index 578c7da3de..40fb46dfd2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py @@ -30,7 +30,7 @@ class Usecase: quantities. See ifcopenshell.api.constraint.add_metric for more information. :return: The newly created IfcObjective entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py index 759fab9a5e..dfc826faf8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py @@ -40,12 +40,12 @@ class Usecase: :param products: The list of products the constraint applies to. This is anything which can have properties or quantities. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance.entity_instance + :type constraint: ifcopenshell.entity_instance :return: The new or updated IfcRelAssociatesConstraint relationship or `None` if `products` was an empty list. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py index 3bb6c852ba..72fead7d88 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py @@ -25,7 +25,7 @@ class Usecase: IfcMetric, consult the IFC documentation. :param metric: The IfcMetric you want to edit. - :type metric: ifcopenshell.entity_instance.entity_instance + :type metric: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py index 96ac354702..dff4985539 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py @@ -25,7 +25,7 @@ class Usecase: IfcObjective, consult the IFC documentation. :param objective: The IfcObjective you want to edit. - :type objective: ifcopenshell.entity_instance.entity_instance + :type objective: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py index d2d797ce0f..e7dab1afb0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py @@ -30,7 +30,7 @@ class Usecase: unclear. :param constraint: The IfcObjective you want to remove. - :type constraint: ifcopenshell.entity_instance.entity_instance + :type constraint: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py index 86ab642b7a..6eaf012fa2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py @@ -25,7 +25,7 @@ class Usecase: and objectives. :param metric: The IfcMetric you want to remove. - :type metric: ifcopenshell.entity_instance.entity_instance + :type metric: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py index dbc1e1b7fb..3b6471713e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py @@ -34,9 +34,9 @@ class Usecase: other products. :param products: The list of products the constraint applies to. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance.entity_instance + :type constraint: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py index e7f96a77d7..02156daf0b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py @@ -102,10 +102,10 @@ class Usecase: :param parent: the parent context. Must be left as None (the default) for contexts, and only set for subcontexts. Note that there are only contexts and subcontexts, a subcontext cannot have any children. - :type parent: ifcopenshell.entity_instance.entity_instance, optional + :type parent: ifcopenshell.entity_instance, optional :return: the newly created IfcGeometricRepresentationContext or IfcGeometricRepresentationSubContext entity - :rtype: ifcopenshell.entity_instance.entity_instance, optional + :rtype: ifcopenshell.entity_instance, optional Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py index 698a8da6ac..50f4612c75 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py @@ -25,7 +25,7 @@ class Usecase: IfcGeometricRepresentationContext, consult the IFC documentation. :param context: The IfcGeometricRepresentationContext entity you want to edit - :type context: ifcopenshell.entity_instance.entity_instance + :type context: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py index edd4f1d723..b0025efbdb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py @@ -27,7 +27,7 @@ class Usecase: removed. If a context is removed, then any subcontexts are also removed. :param context: The IfcGeometricRepresentationContext entity to remove - :type context: ifcopenshell.entity_instance.entity_instance + :type context: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py index 867089137f..4d1ecb128d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -37,12 +37,12 @@ class Usecase: :param relating_control: The IfcControl entity that is creating the control or constraint - :type relating_control: ifcopenshell.entity_instance.entity_instance + :type relating_control: ifcopenshell.entity_instance :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToControl. If relationship already existed before and wasn't changed then returns None. - :rtype: ifcopenshell.entity_instance.entity_instance, None + :rtype: ifcopenshell.entity_instance, None Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py index 33ee89db4d..72996ad62a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py @@ -27,12 +27,12 @@ class Usecase: :param relating_control: The IfcControl entity that is creating the control or constraint - :type relating_control: ifcopenshell.entity_instance.entity_instance + :type relating_control: ifcopenshell.entity_instance :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: If the control still is related to other objects, the IfcRelAssignsToControl is returned, otherwise None. - :rtype: ifcopenshell.entity_instance.entity_instance, None + :rtype: ifcopenshell.entity_instance, None Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py index 0674d1def4..26a0bba442 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py @@ -29,13 +29,13 @@ class Usecase: :param cost_schedule: If the cost item is to be added as a root or top level cost item to a cost schedule, the IfcCostSchedule may be specified. This is mutually exlclusive to the cost_item parameter. - :type cost_schedule: ifcopenshell.entity_instance.entity_instance + :type cost_schedule: ifcopenshell.entity_instance :param cost_item: If the cost item is to be added as a subitem to an existing cost item, the parent IfcCostItem may be specified. This is mutually exclusive to the cost_schedule parameter. - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :return: The newly created IfcCostItem - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index 3402c1828e..fabe443460 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -50,12 +50,12 @@ class Usecase: using another API call. :param cost_item: The IfcCostItem to add the quantity to - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param ifc_class: The type of quantity to add :type ifc_class: str, optional :return: The newly created quantity entity, chosen from the ifc_class parameter - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py index 1d9882ff45..fa97a893f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py @@ -43,7 +43,7 @@ class Usecase: IfcCostScheduleTypeEnum :type predefined_type: str, optional :return: The newly created IfcCostSchedule entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py index edc84d7fc3..e7a481e8b2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py @@ -46,9 +46,9 @@ class Usecase: :param parent: A parent IfcCostItem, if specifying a price directly to a cost item, or a top-level price component. Alternatively, this can be set to a IfcCostValue, if specifying price subcomponents. - :type parent: ifcopenshell.entity_instance.entity_instance + :type parent: ifcopenshell.entity_instance :return: The newly created IfcCostValue - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index b09e7f07fa..d4c6b6be69 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -40,9 +40,9 @@ class Usecase: ifcopenshell.api.control.assign_control. :param cost_item: The IfcCostItem to assign parametric quantities to - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param products: The IfcObjects to assign parametric quantities to - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param prop_name: The name of the quantity. If this is not specified, then it is assumed that there is no calculated quantity, and the number of objects are counted instead. diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py index fd814b4dd5..fb89fe7432 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py @@ -35,9 +35,9 @@ class Usecase: rates as a "template" to quickly populate your rates from. :param cost_item: The IfcCostItem that you want to copy the values to - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param cost_rate: The IfcCostItem that you want to copy the values from - :type cost_rate: ifcopenshell.entity_instance.entity_instance + :type cost_rate: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py index 2127bde2a6..ec2d147731 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py @@ -32,9 +32,9 @@ class Usecase: * The copy will have duplicated nested cost items :param cost_item: The cost item to be duplicated - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :return: The duplicated cost item or the list of duplicated cost items if the latter has children - :rtype: ifcopenshell.entity_instance.entity_instance or list of ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance Example: .. code:: python diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py index a931b52c52..6bc0677b28 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py @@ -29,9 +29,9 @@ class Usecase: parametrically linked, so if one value changes, the other will not. :param source: The IfcCostItem to copy cost values from - :type source: ifcopenshell.entity_instance.entity_instance + :type source: ifcopenshell.entity_instance :param destination: The IfcCostItem to copy cost values from - :type destination: ifcopenshell.entity_instance.entity_instance + :type destination: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py index 3b570ef388..cc0a187177 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py @@ -25,7 +25,7 @@ class Usecase: IfcCostItem, consult the IFC documentation. :param cost_item: The IfcCostItem entity you want to edit - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py index 2684d1e394..3ba4e9f498 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py @@ -25,7 +25,7 @@ class Usecase: IfcPhysicalQuantity, consult the IFC documentation. :param physical_quantity: The IfcPhysicalQuantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance.entity_instance + :type physical_quantity: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py index ab38f62d8e..bdfb856cc1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py @@ -25,7 +25,7 @@ class Usecase: IfcCostSchedule, consult the IFC documentation. :param cost_schedule: The IfcCostSchedule entity you want to edit - :type cost_schedule: ifcopenshell.entity_instance.entity_instance + :type cost_schedule: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py index 818cd4a84b..75ce055eb1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py @@ -29,7 +29,7 @@ class Usecase: IfcCostValue, consult the IFC documentation. :param cost_value: The IfcCostValue entity you want to edit - :type cost_value: ifcopenshell.entity_instance.entity_instance + :type cost_value: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py index ede08a966d..8dada5dc98 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py @@ -33,7 +33,7 @@ class Usecase: For more information, see ifcopenshell.util.cost :param cost_value: The IfcCostValue to set the values of - :type cost_value: ifcopenshell.entity_instance.entity_instance + :type cost_value: ifcopenshell.entity_instance :param formula: The formula following the language of ifcopenshell.util.cost :type formula: str :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py index 06d1f2ef26..5596ceff13 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py @@ -30,7 +30,7 @@ class Usecase: retained. :param cost_item: The IfcCostItem entity you want to remove - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py index bf562c46e7..fae8e1cd37 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py @@ -26,9 +26,9 @@ class Usecase: removed. :param cost_item: The IfcCostItem that the quantity is assigned to - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param physical_quantity: The IfcPhysicalQuantity to remove - :type physical_quantity: ifcopenshell.entity_instance.entity_instance + :type physical_quantity: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py index 79438a5461..51feebb76e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py @@ -29,7 +29,7 @@ class Usecase: including all cost items. :param cost_schedule: The IfcCostSchedule entity you want to remove - :type cost_schedule: ifcopenshell.entity_instance.entity_instance + :type cost_schedule: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py index 6c4a346f02..4af7322899 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py @@ -26,9 +26,9 @@ class Usecase: :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue that the IfcCostValue is assigned to. - :type parent: ifcopenshell.entity_instance.entity_instance + :type parent: ifcopenshell.entity_instance :param cost_value: The IfcCostValue that you want to remove - :type parent: ifcopenshell.entity_instance.entity_instance + :type parent: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py index 967cec4162..091029f594 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py @@ -29,10 +29,10 @@ class Usecase: have any impact on the cost item. :param cost_item: The IfcCostItem to remove quantities from - :type cost_item: ifcopenshell.entity_instance.entity_instance + :type cost_item: ifcopenshell.entity_instance :param products: A list of IfcProducts that may have parametrically connected quantities to the cost item - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py index 7106fe7f7b..68c9c53759 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py @@ -33,9 +33,9 @@ class Usecase: is considered the latest version and the children are older revisions. :param parent: The parent document, if necessary. - :type parent: ifcopenshell.entity_instance.entity_instance, optional + :type parent: ifcopenshell.entity_instance, optional :return: The newly created IfcDocumentInformation entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py index bcec5da606..80cf91d8a1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py @@ -39,9 +39,9 @@ class Usecase: :param information: The IfcDocumentInformation that the reference will be created for - :type information: ifcopenshell.entity_instance.entity_instance + :type information: ifcopenshell.entity_instance :return: The newly created IfcDocumentReference entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py index f67cbe890a..f9b433213a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py @@ -42,15 +42,15 @@ class Usecase: :param product: The list of objects to associate the document to. This could be almost any sensible object in IFC. - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :param document: The IfcDocumentReference to associate to, or alternatively an IfcDocumentInformation, though this is not recommended. - :type document: ifcopenshell.entity_instance.entity_instance + :type document: ifcopenshell.entity_instance :return: The IfcRelAssociatesDocument relationship or `None` if `products` was an empty list or all products were already assigned to the `document`. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py index 1d7af0c1b8..96c0120120 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py @@ -32,7 +32,7 @@ class Usecase: IfcDocumentInformation, consult the IFC documentation. :param reference: The IfcDocumentInformation entity you want to edit - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py index 538c8d2854..d88afdfc2f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py @@ -32,7 +32,7 @@ class Usecase: IfcDocumentReference, consult the IFC documentation. :param reference: The IfcDocumentReference entity you want to edit - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py index bf26837822..56f57df283 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py @@ -29,7 +29,7 @@ class Usecase: All references and associations are also removed. :param information: The IfcDocumentInformation to remove - :type information: ifcopenshell.entity_instance.entity_instance + :type information: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py index 6eee90fb4d..61fd6810c1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py @@ -27,7 +27,7 @@ class Usecase: All associations with objects are removed. :param reference: The IfcDocumentReference to remove - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py index dd43573e65..c4728d5511 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py @@ -32,10 +32,10 @@ class Usecase: :param product: The list of objects that the document reference or information is related to. - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :param document: The IfcDocumentReference (typically) or in rare cases the IfcDocumentInformation that is associated with the product - :type document: ifcopenshell.entity_instance.entity_instance + :type document: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py index 11da9c0ee0..051dd985b3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py @@ -42,12 +42,12 @@ class Usecase: in 3D. :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance.entity_instance + :type relating_product: ifcopenshell.entity_instance :param related_object: The object (typically IfcAnnotation) that the product is related to - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py index c8be879f7f..00b8bc4f82 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py @@ -25,7 +25,7 @@ class Usecase: IfcTextLiteral, consult the IFC documentation. :param reference: The IfcTextLiteral entity you want to edit - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py index 8b0e514935..91ee7ded3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py @@ -31,12 +31,12 @@ class Usecase: object later or leave the annotation as a "dumb" annotation. :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance.entity_instance + :type relating_product: ifcopenshell.entity_instance :param related_object: The object (typically IfcAnnotation) that the product is related to - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py index 9ed90b6fb7..8a09531a26 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py @@ -52,13 +52,13 @@ class Usecase: :param context: The IfcGeometricRepresentationContext that the representation is part of. This must be either a Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D). - :type context: ifcopenshell.entity_instance.entity_instance + :type context: ifcopenshell.entity_instance :param axis: The axis, as a list of two coordinates, the coordinates being either a list of 2 or 3 float coordinates depending on whether the axis is 2D or 3D. :type axis: list[list[float]] :return: The newly created IfcShapeRepresentation entity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py index a09e7b2bd2..21fead74e5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -35,7 +35,7 @@ class Usecase: single edge. :type axis_curve: bpy.types.Object :param grid_axis: The IfcGridAxis element to add geometry to. - :type grid_axis: ifcopenshell.entity_instance.entity_instance + :type grid_axis: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py index e794cbdc86..de667bb5bc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py @@ -54,9 +54,9 @@ class Usecase: Defaults to "UAxes". :type uvw_axes: str, optional :param grid: The IfcGrid you are adding the axis to. - :type grid: ifcopenshell.entity_instance.entity_instance + :type grid: ifcopenshell.entity_instance :return: The newly created IfcGridAxis - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py index d14ee8cf0b..b380778a67 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py @@ -24,7 +24,7 @@ class Usecase: """Removes a grid axis from a grid :param axis: The IfcGridAxis you want to remove. - :type axis: ifcopenshell.entity_instance.entity_instance + :type axis: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py index fa2877eac5..298ec42ba6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py @@ -37,7 +37,7 @@ class Usecase: :param Description: The description of the purpose of the group. :type Description: str, optional :return: The newly created IfcGroup - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py index 78d787f741..d312a95bd6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py @@ -31,12 +31,12 @@ class Usecase: twice. :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance.entity_instance + :type group: ifcopenshell.entity_instance :return: The IfcRelAssignsToGroup relationship or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py index 442b9d84f6..87fa9dcf12 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py @@ -25,7 +25,7 @@ class Usecase: IfcGroup, consult the IFC documentation. :param group: The IfcGroup entity you want to edit - :type group: ifcopenshell.entity_instance.entity_instance + :type group: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py index 0ade115de1..c87b36e316 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py @@ -29,7 +29,7 @@ class Usecase: the group will be removed. :param group: The IfcGroup entity you want to remove - :type group: ifcopenshell.entity_instance.entity_instance + :type group: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py index 57d8d88556..9229281a69 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py @@ -28,9 +28,9 @@ class Usecase: If the product isn't assigned to the group, nothing will happen. :param products: A list of IfcProduct elements to unassign from the group - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param group: The IfcGroup to unassign from - :type group: ifcopenshell.entity_instance.entity_instance + :type group: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py index a752057f84..61b96c2ba6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py @@ -28,11 +28,11 @@ class Usecase: removed. :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance.entity_instance + :type group: ifcopenshell.entity_instance :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py index 848dc423ce..8638a76b22 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py @@ -36,7 +36,7 @@ class Usecase: :param Name: The name of the layer. Defaults to "Unnamed". :type Name: str, optional :return: The newly created IfcPresentationLayerAssignment element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py index 21a4ceb2bf..93a863d66f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py @@ -34,10 +34,10 @@ class Usecase: :param items: The list of IfcRepresentationItems to assign to the layer. This should be the items from the object's IfcShapeRepresentation. - :type items: list[ifcopenshell.entity_instance.entity_instance] + :type items: list[ifcopenshell.entity_instance] :param layer: The IfcPresentationLayerAssignment layer to assign the item to. - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py index 4ac74d4014..c2b1cbc99a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py @@ -25,7 +25,7 @@ class Usecase: IfcPresentationLayerAssignment, consult the IFC documentation. :param layer: The IfcPresentationLayerAssignment entity you want to edit - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py index 2834d8f8ef..8e83e475ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py @@ -25,7 +25,7 @@ class Usecase: relationship to the layer will be removed. :param layer: The IfcPresentationLayerAssignment entity to remove - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py index 657db6ac9e..f9d6a024a3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py @@ -32,9 +32,9 @@ class Usecase: removed to keep IFC valid. :param items: A list IfcRepresentationItem elements to unassign - :type items: list[ifcopenshell.entity_instance.entity_instance] + :type items: list[ifcopenshell.entity_instance] :param layer: The IfcPresentationLayerAssignment to unassign from - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py index db011f5ca0..f20494ac5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py @@ -53,7 +53,7 @@ class Usecase: :param name: The name of the library :type name: str :return: The newly created IfcLibraryInformation - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py index 0a109b2118..84f6605cf0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py @@ -36,9 +36,9 @@ class Usecase: library's references. :param library: The IfcLibraryInformation element to add a reference to - :type library: ifcopenshell.entity_instance.entity_instance + :type library: ifcopenshell.entity_instance :return: The newly created IfcLibraryReference element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py index 8ee4a21eb5..8a0880ccf0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py @@ -33,14 +33,14 @@ class Usecase: detail about how references work. :param products: The list of IfcProducts you want to associate with the reference - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param reference: The IfcLibraryReference you want the product to be associated with. - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :return: The IfcRelAssociatesLibrary relationship entity or `None` if `products` was an empty list or all products were already assigned to the `reference`. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py index a5846f0c9b..aca508cc38 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py @@ -25,7 +25,7 @@ class Usecase: IfcLibraryInformation, consult the IFC documentation. :param library: The IfcLibraryInformation entity you want to edit - :type library: ifcopenshell.entity_instance.entity_instance + :type library: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py index 4fb13253a8..35a1be4709 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py @@ -25,7 +25,7 @@ class Usecase: IfcLibraryReference, consult the IFC documentation. :param reference: The IfcLibraryReference entity you want to edit - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py index 931d9153d6..e921016c4d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py @@ -28,7 +28,7 @@ class Usecase: products which have relationships to this library will not be removed. :param library: The IfcLibraryInformation entity you want to remove - :type library: ifcopenshell.entity_instance.entity_instance + :type library: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py index 9a5851827a..d34973f6b2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py @@ -28,7 +28,7 @@ class Usecase: removed. :param reference: The IfcLibraryReference entity you want to remove - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py index b650ffba75..420b7fa0d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py @@ -33,9 +33,9 @@ class Usecase: If the product isn't assigned to the reference, nothing will happen. :param reference: The IfcLibraryReference to unassign from - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :param products: A list of IfcProduct elements to unassign from the reference - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py index c45b5fb14d..278eb50872 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py @@ -38,11 +38,11 @@ class Usecase: constituent is part of. The constituent set represents a group of constituents. See ifcopenshell.api.material.add_material_set for information on how to add a constituent set. - :type constituent_set: ifcopenshell.entity_instance.entity_instance + :type constituent_set: ifcopenshell.entity_instance :param material: The IfcMaterial that the constituent is made out of. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: The newly created IfcMaterialConstituent - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py index d0b36b4ec6..aa572f07bd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py @@ -36,11 +36,11 @@ class Usecase: layer set represents a group of layers. See ifcopenshell.api.material.add_material_set for more information on how to add a layer set. - :type layer_set: ifcopenshell.entity_instance.entity_instance + :type layer_set: ifcopenshell.entity_instance :param material: The IfcMaterial that the layer is made out of. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: The newly created IfcMaterialLayer - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py index 92b873b198..9a12ed044b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py @@ -39,9 +39,9 @@ class Usecase: :param material_list: The IfcMaterialList the material should be added to. - :type material_list: ifcopenshell.entity_instance.entity_instance + :type material_list: ifcopenshell.entity_instance :param material: The IfcMaterial to add to the list - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py index 3959e4becd..bac5a3ac0a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py @@ -53,7 +53,7 @@ class Usecase: :param category: The category of the material. :type category: str, optional :return: The newly created IfcMaterial - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py index 5ec8a36623..277aa258f2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py @@ -64,7 +64,7 @@ class Usecase: IfcMaterialConstituentSet. :type set_type: str, optional :return: The newly created material set element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py index 51b622b3f3..ff2cf3bed5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py @@ -47,14 +47,14 @@ class Usecase: profile set represents a group of profile items. See ifcopenshell.api.material.add_material_set for more information on how to add a profile set. - :type profile_set: ifcopenshell.entity_instance.entity_instance + :type profile_set: ifcopenshell.entity_instance :param material: The IfcMaterial that the profile item is made out of. - :type material: ifcopenshell.entity_instance.entity_instance, optional + :type material: ifcopenshell.entity_instance, optional :param profile: The IfcProfileDef that represents the 2D cross section of the the profile item. - :type profile: ifcopenshell.entity_instance.entity_instance, optional + :type profile: ifcopenshell.entity_instance, optional :return: The newly created IfcMaterialProfile - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py index 0445f2a995..a65a644866 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py @@ -64,7 +64,7 @@ class Usecase: :param products: The list of IfcProducts to assign the material or material set to. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or @@ -74,15 +74,15 @@ class Usecase: :param material: The IfcMaterial or material set you are assigning here. If type is Usage then no need to provide `material`, it will be deduced from the element type automatically. - :type material: ifcopenshell.entity_instance.entity_instance, optional + :type material: ifcopenshell.entity_instance, optional :return: IfcRelAssociatesMaterial entity or a list of IfcRelAssociatesMaterial entities (possible if `type` is Usage and `products` require different Usages) or `None` if `products` was empty list. :rtype: Union[ - ifcopenshell.entity_instance.entity_instance, - list[ifcopenshell.entity_instance.entity_instance], None] + ifcopenshell.entity_instance, + list[ifcopenshell.entity_instance], None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 4acd6637e7..4d1678a0ae 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -29,9 +29,9 @@ class Usecase: :param material_profile: The IfcMaterialProfile to change the profile curve of. See ifcopenshell.api.material.add_profile to see how to create profiles. - :type material_profile: ifcopenshell.entity_instance.entity_instance + :type material_profile: ifcopenshell.entity_instance :param profile: The IfcProfileDef to set the profile item's curve to. - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py index cd49c73e8f..8dac43b8e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py @@ -28,9 +28,9 @@ class Usecase: associated to any elements. :param material: The IfcMaterial to copy - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: The new copy of the material - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py index 3e481d9748..3e3a03dbd8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py @@ -25,7 +25,7 @@ class Usecase: IfcMaterial, consult the IFC documentation. :param element: The IfcMaterial entity you want to edit - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py index 22660fcb24..ef036527a3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py @@ -25,11 +25,11 @@ class Usecase: IfcMaterialConstituent, consult the IFC documentation. :param constituent: The IfcMaterialConstituent entity you want to edit - :type constituent: ifcopenshell.entity_instance.entity_instance + :type constituent: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :param material: The IfcMaterial entity you want to change the constituent to - :type material: ifcopenshell.entity_instance.entity_instance, optional + :type material: ifcopenshell.entity_instance, optional :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py index e6981d059b..3e31194452 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py @@ -25,12 +25,12 @@ class Usecase: IfcMaterialLayer, consult the IFC documentation. :param layer: The IfcMaterialLayer entity you want to edit - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :param material: The IfcMaterial entity you want the layer to be made from. - :type material: ifcopenshell.entity_instance.entity_instance, optional + :type material: ifcopenshell.entity_instance, optional :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py index 3e45c68c78..a30fa39f21 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py @@ -28,7 +28,7 @@ class Usecase: IfcMaterialLayerSetUsage, consult the IFC documentation. :param usage: The IfcMaterialLayerSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance.entity_instance + :type usage: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py index 04de2044cc..6fc781f9a6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -25,15 +25,15 @@ class Usecase: IfcMaterialProfile, consult the IFC documentation. :param profile: The IfcMaterialProfile entity you want to edit - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :param profile_def: The IfcProfileDef entity the profile curve should be extruded from. - :type profile_def: ifcopenshell.entity_instance.entity_instance, optional + :type profile_def: ifcopenshell.entity_instance, optional :param material: The IfcMaterial entity you want to change the profile to be made from. - :type material: ifcopenshell.entity_instance.entity_instance, optional + :type material: ifcopenshell.entity_instance, optional :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py index edd6f27fee..afd9007c7b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py @@ -33,7 +33,7 @@ class Usecase: IfcMaterialProfileSetUsage, consult the IFC documentation. :param usage: The IfcMaterialProfileSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance.entity_instance + :type usage: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py index b62d06bee7..2fb919d10d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py @@ -25,7 +25,7 @@ class Usecase: at least one constituent to ensure a valid IFC dataset. :param constituent: The IfcMaterialConstituent entity you want to remove - :type constituent: ifcopenshell.entity_instance.entity_instance + :type constituent: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py index 3d6d8b8df6..f068641533 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py @@ -25,7 +25,7 @@ class Usecase: at least one layer to ensure a valid IFC dataset. :param layer: The IfcMaterialLayer entity you want to remove - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py index 47269b51dc..276d9e64d2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -28,7 +28,7 @@ class Usecase: :param material_list: The IfcMaterialList entity you want to remove an item from. - :type material_list: ifcopenshell.entity_instance.entity_instance + :type material_list: ifcopenshell.entity_instance :param material_index: The index of the material you want to remove from the list. Starts counting at 0. Defaults to 0. :type material_index: int, optional diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py index 5c048734c7..ffdf9693d8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py @@ -30,7 +30,7 @@ class Usecase: take care of this situation themselves. :param material: The IfcMaterial entity you want to remove - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py index 9eb7bcab62..79093789aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py @@ -30,7 +30,7 @@ class Usecase: :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, IfcMaterialProfileSet entity you want to remove. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py index 4174d067cb..9c930866ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py @@ -29,7 +29,7 @@ class Usecase: at least one profile to ensure a valid IFC dataset. :param profile: The IfcMaterialProfile entity you want to remove - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py index d6e2d6f9f7..442fec8b5e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py @@ -26,7 +26,7 @@ class Usecase: :param material_set: The IfcMaterialSet which you want to reorder an item in. - :type material_set: ifcopenshell.entity_instance.entity_instance + :type material_set: ifcopenshell.entity_instance :param old_index: The index of the item you want to move. This starts counting from 0. :type old_index: int diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py index 60fac1382e..5f88963c36 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py @@ -32,7 +32,7 @@ class Usecase: If the product does not have a material, nothing happens. :param products: The list IfcProducts that may or may not have a material - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py index 5617d39f06..1c9ab8c9b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py @@ -80,13 +80,13 @@ class Usecase: :param related_objects: The list of children of the nesting relationship, typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance.entity_instance] + :type related_objects: list[ifcopenshell.entity_instance] :param relating_object: The host parent of the nesting relationship, typically an IfcElement. - :type relating_object: ifcopenshell.entity_instance.entity_instance + :type relating_object: ifcopenshell.entity_instance :return: The IfcRelNests relationship instance or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py index 1c986f2cd0..b9f48b4b5b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py @@ -32,7 +32,7 @@ class Usecase: :param related_objects: The list of children of the nesting relationship, typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance.entity_instance] + :type related_objects: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py index 9325638d96..c3ff65c3d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py @@ -42,11 +42,11 @@ class Usecase: IfcPerson if it is a sole individual, or an IfcPersonAndOrganization if a specific person is liable within an organisation and must be legally nominated. - :type actor: ifcopenshell.entity_instance.entity_instance + :type actor: ifcopenshell.entity_instance :param ifc_class: Either "IfcActor" or "IfcOccupant". :type ifc_class: str, optional :return: The newly created IfcActor or IfcOccupant - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py index 3a9c3e6c14..b184408fa9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py @@ -33,12 +33,12 @@ class Usecase: :param assigned_object: The IfcOrganization or IfcPerson the contact address belongs to. - :type assigned_object: ifcopenshell.entity_instance.entity_instance + :type assigned_object: ifcopenshell.entity_instance :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults to IfcPostalAddress. :type ifc_class: str, optional :return: The new IfcPostalAddress or IfcTelecomAddress - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py index 792096ef9d..92861a3417 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py @@ -39,7 +39,7 @@ class Usecase: :param application_developer: The IfcOrganization responsible for creating the application. Defaults to generating an IfcOpenShell organisation if none is provided. - :type application_developer: ifcopenshell.entity_instance.entity_instance, optional + :type application_developer: ifcopenshell.entity_instance, optional :param version: The version of the application. Defaults to the ifcopenshell.version data if not specified. :type version: str, optional diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py index 0e6f676fbc..2127acd570 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py @@ -34,7 +34,7 @@ class Usecase: :param name: The legal name of the organisation :type name: str, optional :return: The newly created IfcOrganization - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py index 268568c80d..a607d8af29 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py @@ -39,7 +39,7 @@ class Usecase: :param given_name: The given name :type given_name: str, optional :return: The newly created IfcPerson - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py index 3e0b372690..a5987b4b76 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py @@ -32,11 +32,11 @@ class Usecase: :param person: The IfcPerson being the representative of the organisation. - :type person: ifcopenshell.entity_instance.entity_instance + :type person: ifcopenshell.entity_instance :param organisation: The IfcOrganization itself. - :type organisation: ifcopenshell.entity_instance.entity_instance + :type organisation: ifcopenshell.entity_instance :return: The newly created IfcPersonAndOrganization - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py index 1a18e3c403..3b1369877c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py @@ -31,12 +31,12 @@ class Usecase: :param assigned_object: The IfcPerson or IfcOrganization the role should be assigned to. - :type assigned_object: ifcopenshell.entity_instance.entity_instance + :type assigned_object: ifcopenshell.entity_instance :param role: The type of role, taken from the IFC documentation for IfcActorRole, or a custom name. :type role: str, optional :return: The newly created IfcActorRole - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py index 395818e922..1085adeb34 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py @@ -41,11 +41,11 @@ class Usecase: ifcopenshell.api.resource.assign_resource. :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance.entity_instance + :type relating_actor: ifcopenshell.entity_instance :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToActor relationship. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py index 31491feeaa..94183e0c6b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py @@ -62,7 +62,7 @@ class Usecase: :return: The newly created IfcOwnerHistory element or `None` if it's not IFC2X3 and user or application is not found in the current project. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py index 87638e6e44..eb6491b359 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py @@ -25,7 +25,7 @@ class Usecase: IfcActor, consult the IFC documentation. :param actor: The IfcActor entity you want to edit - :type actor: ifcopenshell.entity_instance.entity_instance + :type actor: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py index 5ecda0ec03..0f48af25d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py @@ -25,7 +25,7 @@ class Usecase: IfcAddress, consult the IFC documentation. :param address: The IfcAddress entity you want to edit - :type address: ifcopenshell.entity_instance.entity_instance + :type address: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py index 03289d6f14..19c8d1e431 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py @@ -25,7 +25,7 @@ class Usecase: IfcOrganization, consult the IFC documentation. :param organisation: The IfcOrganization entity you want to edit - :type organisation: ifcopenshell.entity_instance.entity_instance + :type organisation: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py index 931cdb0c82..19eedc23db 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py @@ -25,7 +25,7 @@ class Usecase: IfcPerson, consult the IFC documentation. :param person: The IfcPerson entity you want to edit - :type person: ifcopenshell.entity_instance.entity_instance + :type person: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py index 6e96df744f..160f6f6d91 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py @@ -25,7 +25,7 @@ class Usecase: IfcActorRole, consult the IFC documentation. :param role: The IfcActorRole entity you want to edit - :type role: ifcopenshell.entity_instance.entity_instance + :type role: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py index 2f48ded614..ac99299a6f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py @@ -25,7 +25,7 @@ class Usecase: """Removes an actor :param actor: The IfcActor to remove. - :type actor: ifcopenshell.entity_instance.entity_instance + :type actor: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py index 1bb9247c0d..728ffb45f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py @@ -25,7 +25,7 @@ class Usecase: relationship removed. :param address: The IfcAddress to remove. - :type address: ifcopenshell.entity_instance.entity_instance + :type address: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py index 63e8092338..7e21c07943 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py @@ -25,7 +25,7 @@ class Usecase: Check whether or not the application is used anywhere prior to removal. :param address: The IfcApplication to remove. - :type address: ifcopenshell.entity_instance.entity_instance + :type address: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py index e5c7b36d8b..e9e2c77e9e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py @@ -27,7 +27,7 @@ class Usecase: removed. :param organisation: The IfcOrganization to remove - :type organisation: ifcopenshell.entity_instance.entity_instance + :type organisation: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py index af82abf989..8e1ba7a972 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py @@ -27,7 +27,7 @@ class Usecase: removed. :param person: The IfcPerson to remove - :type person: ifcopenshell.entity_instance.entity_instance + :type person: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py index 6e12917d72..fc85722e68 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py @@ -27,7 +27,7 @@ class Usecase: the "person and organisation" group. :param person_and_organisation: The IfcPersonAndOrganization to remove. - :type person_and_organisation: ifcopenshell.entity_instance.entity_instance + :type person_and_organisation: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py index 3e6a6cc721..5de9915c33 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py @@ -25,7 +25,7 @@ class Usecase: leave some of them without roles. :param role: The IfcActorRole to remove. - :type role: ifcopenshell.entity_instance.entity_instance + :type role: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/settings.py b/src/ifcopenshell-python/ifcopenshell/api/owner/settings.py index 6c0e6a456f..493f4077f4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/settings.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/settings.py @@ -30,9 +30,9 @@ def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instanc IfcApplication. See ifcopenshell.api.owner.create_owner_history for details. :param ifc: The IFC file object that is being edited. - :type ifc: ifcopenshell.file.file + :type ifc: ifcopenshell.file :return: The IfcApplication with metadata of the authoring software. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ app = ifc.by_type("IfcApplication") if not app and ifc.schema == "IFC2X3": @@ -50,9 +50,9 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None IfcApplication. See ifcopenshell.api.owner.create_owner_history for details. :param ifc: The IFC file object that is being edited. - :type ifc: ifcopenshell.file.file + :type ifc: ifcopenshell.file :return: The IfcPersonAndOrganization with metadata of the authoring user. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ pao = ifc.by_type("IfcPersonAndOrganization") if not pao and ifc.schema == "IFC2X3": diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py index aadb6426c9..711732bcdc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py @@ -28,12 +28,12 @@ class Usecase: This means that the actor is no longer responsible for the object. :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance.entity_instance + :type relating_actor: ifcopenshell.entity_instance :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The updated IfcRelAssignsToActor relationship or none if there is no more valid relationship. - :rtype: None, ifcopenshell.entity_instance.entity_instance + :rtype: None, ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py index 27230372fb..797d1b8b57 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py @@ -34,9 +34,9 @@ class Usecase: :param element: The IfcRoot element to update the ownership details on when a change is made. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The updated IfcOwnerHistory element. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py index edf1d22408..8cced9d934 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py @@ -37,7 +37,7 @@ class Usecase: this may be left as none. :type name: str, optional :return: The newly created IfcArbitraryClosedProfileDef - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py index a33b4d38be..7c1af83294 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py @@ -44,7 +44,7 @@ class Usecase: this may be left as none. :type name: str, optional :return: The newly created IfcArbitraryProfileDefWithVoids - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py index 6b8c3786c6..0201095feb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py @@ -26,13 +26,13 @@ class Usecase: the IFC documentation as subclasses of IfcParameterizedProfileDef. Currently, this API has no benefit over directly calling - ifcopenshell.file.file.create_entity. + ifcopenshell.file.create_entity. :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd like to create. :type ifc_class: str :return: The newly created element depending on the specified ifc_class. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py index 489bf0b5fa..759a5d4c7d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py @@ -25,7 +25,7 @@ class Usecase: IfcProfileDef, consult the IFC documentation. :param profile: The IfcProfileDef entity you want to edit - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py index ce037686e1..47d3391e00 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py @@ -25,7 +25,7 @@ class Usecase: """Removes a profile :param profile: The IfcProfileDef to remove. - :type profile: ifcopenshell.entity_instance.entity_instance + :type profile: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index b3640ca53e..3e88c7c82a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -39,18 +39,18 @@ class Usecase: Do not mix units. :param library: The file object containing the asset. - :type library: ifcopenshell.file.file + :type library: ifcopenshell.file :param element: An element in the library file of the asset. It may be an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or IfcProfileDef. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param reuse_identities: Optional dictionary of mapped entities' identities to the already created elements. It will be used to avoid creating duplicated inverse elements during multiple `project.append_asset` calls. If you want to add just 1 asset or if added assets won't have any shared elements, then it can be left empty. - :type reuse_identities: dict[int, ifcopenshell.entity_instance.entity_instance] + :type reuse_identities: dict[int, ifcopenshell.entity_instance] :return: The appended element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py index be4d076de0..e6e919b77b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -43,13 +43,13 @@ class Usecase: a declaration lets you say that an object belongs to a library. :param definitions: The list of objects you want to declare. Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance.entity_instance] + :type definitions: list[ifcopenshell.entity_instance] :param relating_context: The IfcProject, or more commonly the IfcProjectLibrary that you want the object to be part of. - :type relating_context: ifcopenshell.entity_instance.entity_instance + :type relating_context: ifcopenshell.entity_instance :return: The new IfcRelDeclares relationship or None if all definitions were already declared / do not support declaration. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py index 45ce3d6c6a..1b3e644cbd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py @@ -37,7 +37,7 @@ class Usecase: schema, you may specify that schema identifier here too. :type version: str, optional :return: The created IFC file object. - :rtype: ifcopenshell.file.file + :rtype: ifcopenshell.file Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py index 47c5194bdd..8f8e726570 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -34,10 +34,10 @@ class Usecase: :param definitions: The list of objects you want to undeclare. Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance.entity_instance] + :type definitions: list[ifcopenshell.entity_instance] :param relating_context: The IfcProject, or more commonly the IfcProjectLibrary that you want the object to no longer be part of. - :type relating_context: ifcopenshell.entity_instance.entity_instance + :type relating_context: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py index ac498e413d..1fd1a0c61a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py @@ -53,7 +53,7 @@ class Usecase: data, rather than arbitrary metadata. :param product: The IfcObject that you want to assign a property set to. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param name: The name of the property set. Property sets that are standardised by buildingSMART typically have a prefix of "Pset_", like "Pset_WallCommon". If you create your own, you must not use @@ -61,7 +61,7 @@ class Usecase: your project, company, or local government requirement. :type name: str :return: The newly created IfcPropertySet - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py index 9e7d9eee78..a3c7b54299 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py @@ -49,7 +49,7 @@ class Usecase: metadata, rather than quantification data. :param product: The IfcObject that you want to assign a quantity set to. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param name: The name of the quantity set. Quantity sets that are standardised by buildingSMART typically have a prefix of "Qto_", like "Qto_WallBaseQuantities". If you create your own, you must not @@ -57,7 +57,7 @@ class Usecase: to your project, company, or local government requirement. :type name: str :return: The newly created IfcElementQuantity - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index 81b57db874..0eb711b803 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -52,7 +52,7 @@ class Usecase: to ensure that data types are always consistent and correct. :param pset: The IfcPropertySet to edit. - :type pset: ifcopenshell.entity_instance.entity_instance + :type pset: ifcopenshell.entity_instance :param name: A new name for the property set. If no name is specified, the property set name is not changed. :type name: str, optional @@ -69,7 +69,7 @@ class Usecase: :param pset_template: If a property set template is provided, this will be used to determine data types. If no user-defined template is provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance.entity_instance + :type pset_template: ifcopenshell.entity_instance :param should_purge: If left as False, properties set to None will be left as None but not removed. If set to true, properties set to None will actually be removed. diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py index 820f8d858f..cd5a3bca05 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py @@ -34,7 +34,7 @@ class Usecase: It is not allowed to have None quantities in IFC. :param qto: The IfcElementQuantity to edit. - :type qto: ifcopenshell.entity_instance.entity_instance + :type qto: ifcopenshell.entity_instance :param name: A new name for the quantity set. If no name is specified, the quantity set name is not changed. :type name: str, optional @@ -51,7 +51,7 @@ class Usecase: :param pset_template: If a quantity set template is provided, this will be used to determine data types. If no user-defined template is provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance.entity_instance + :type pset_template: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py index b71be041f8..da77accbb6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py @@ -27,9 +27,9 @@ class Usecase: All properties that are part of this property set are also removed. :param product: The IfcObject to remove the property set from. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param pset: The IfcPropertySet or IfcElementQuantity to remove. - :type pset: ifcopenshell.entity_instance.entity_instance + :type pset: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py index 569b2bcdd5..5a9dc42355 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py @@ -57,7 +57,7 @@ class Usecase: :param pset_template: The property set template to add the property template to. - :type pset_template: ifcopenshell.entity_instance.entity_instance + :type pset_template: ifcopenshell.entity_instance :param name: The name of the property :type name: str,optional :param description: A few words describing what the property stores. @@ -66,7 +66,7 @@ class Usecase: IFC documentation for the full list of data types. :param primary_measure_type: str,optional :return: The newly created IfcSimplePropertyTemplate. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py index 4611dfb48e..242f7b7510 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py @@ -84,7 +84,7 @@ class Usecase: property set may be assigned to any type. :type applicable_entity: str,optional :return: The newly created IfcPropertySetTemplate - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py index 1ddc18f7dc..6b2633992f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py @@ -25,7 +25,7 @@ class Usecase: IfcSimplePropertyTemplate, consult the IFC documentation. :param prop_template: The IfcSimplePropertyTemplate entity you want to edit - :type prop_template: ifcopenshell.entity_instance.entity_instance + :type prop_template: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py index 29d99753a4..303618f509 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py @@ -25,7 +25,7 @@ class Usecase: IfcPropertySetTemplate, consult the IFC documentation. :param pset_template: The IfcPropertySetTemplate entity you want to edit - :type pset_template: ifcopenshell.entity_instance.entity_instance + :type pset_template: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py index 1bb5650dfc..6479e6ffc2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py @@ -28,7 +28,7 @@ class Usecase: templates. :param prop_template: The IfcSimplePropertyTemplate to remove. - :type prop_template: ifcopenshell.entity_instance.entity_instance + :type prop_template: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py index 93bb120dbe..4cb2c2695a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py @@ -27,7 +27,7 @@ class Usecase: along with it. :param pset_template: The IfcPropertySetTemplate to remove. - :type pset_template: ifcopenshell.entity_instance.entity_instance + :type pset_template: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py index f2c3bf99ee..03a67ab648 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py @@ -50,7 +50,7 @@ class Usecase: :param parent_resource: If this is a child resource (typically to a crew resource), then nominate the parent IfcConstructionResource here. - :type parent_resource: ifcopenshell.entity_instance.entity_instance + :type parent_resource: ifcopenshell.entity_instance :param ifc_class: The class of resource chosen from IfcConstructionEquipmentResource, IfcConstructionMaterialResource, IfcConstructionProductResource, IfcCrewResource, IfcLaborResource, @@ -63,7 +63,7 @@ class Usecase: :type predefined_type: str,optional :return: The newly created resource depending on the nominated IFC class. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py index 55fb80902d..4e5ef0c0a0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -34,7 +34,7 @@ class Usecase: This base quantity is then used in other calculations. :param resource: The IfcConstructionResource to add a quantity to. - :type resource: ifcopenshell.entity_instance.entity_instance + :type resource: ifcopenshell.entity_instance :param ifc_class: The type of quantity to add, chosen from IfcQuantityArea (for material), IfcQuantityCount (for products), IfcQuantityLength (for material), IfcQuantityTime (for equipment or @@ -42,7 +42,7 @@ class Usecase: (for material). :type ifc_class: str,optional :return: The newly created quantity depending on the IFC class - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py index 8407555e35..8627e319a7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py @@ -29,9 +29,9 @@ class Usecase: be used to calculate other parameters like resource utilisation. :param resource: The IfcConstructionResource to record time for. - :type resource: ifcopenshell.entity_instance.entity_instance + :type resource: ifcopenshell.entity_instance :return: The newly created IfcResourceTime - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py index 121a565177..f71ec00260 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py @@ -38,12 +38,12 @@ class Usecase: (e.g. if the resource is a labour resource). :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance.entity_instance + :type relating_resource: ifcopenshell.entity_instance :param related_object: The IfcProduct or IfcActor to assign to the object. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py index a23cddd09e..746f0d88c0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py @@ -52,7 +52,7 @@ class Usecase: :param resource: The IfcConstructionResource that you want to calculate the work performed. - :type resource: ifcopenshell.entity_instance.entity_instance + :type resource: ifcopenshell.entity_instance :return None: :rtype: None: """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py index 34be65fea5..c28f4c0661 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py @@ -25,7 +25,7 @@ class Usecase: IfcResource, consult the IFC documentation. :param resource: The IfcResource entity you want to edit - :type resource: ifcopenshell.entity_instance.entity_instance + :type resource: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py index f88ed466fd..0785caa02e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -25,7 +25,7 @@ class Usecase: IfC quantity, consult the IFC documentation. :param physical_quantity: The IfC quantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance.entity_instance + :type physical_quantity: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py index 6a4b2029d4..c9db827a89 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py @@ -28,7 +28,7 @@ class Usecase: IfcResourceTime, consult the IFC documentation. :param resource_time: The IfcResourceTime entity you want to edit - :type resource_time: ifcopenshell.entity_instance.entity_instance + :type resource_time: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py index 50c8661ecd..ceed0dbf2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py @@ -26,12 +26,12 @@ class Usecase: """Removes the relationship between a resource and object :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance.entity_instance + :type relating_resource: ifcopenshell.entity_instance :param related_object: The IfcProduct or IfcActor to assign to the object. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index 7ffbdc38fa..389930d409 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -50,9 +50,9 @@ class Usecase: connections are still valid. :param product: The IfcProduct to copy. - :type param: ifcopenshell.entity_instance.entity_instance + :type param: ifcopenshell.entity_instance :return: The copied product - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py index 8599c00472..7619eec067 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py @@ -52,7 +52,7 @@ class Usecase: :param name: The name of the new element. :type name: str,optional :return: The newly created element based on the specified IFC class. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py index c87df1e8e4..1035a6b17c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -48,14 +48,14 @@ class Usecase: this. :param product: The IfcProduct that you want to change the class of. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param ifc_class: The new IFC class you want to change it to. :type ifc_class: str,optional :param predefined_type: In case you want to change the predefined type too. User defined types are also allowed, just type what you want. :type predefined_type: str,optional :return: The newly modified product. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py index 179e7ca572..cecb5544f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py @@ -42,7 +42,7 @@ class Usecase: naturally, the materials, types, containers, etc themselves remain). :param product: The element to remove. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py index bf42853d2c..60ab48c6df 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py @@ -67,11 +67,11 @@ class Usecase: :param work_schedule: The work schedule to group the task in, if the task is to be a top-level or root task. This is mutually exclusive with the parent_task parameter. - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :param parent_task: The parent task, if the task is to be a subtask or child task. This is mutually exclusive with the work_schedule parameter. - :type parent_task: ifcopenshell.entity_instance.entity_instance + :type parent_task: ifcopenshell.entity_instance :param name: The name of the task. :type name: str,optional :param description: The description of the task. @@ -83,7 +83,7 @@ class Usecase: IFC documentation for IfcTaskTypeEnum for more information. :type predefined_type: str :return: The newly created IfcTask - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py index a1638d2b04..7c4381c361 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py @@ -27,11 +27,11 @@ class Usecase: (especially for maintenance tasks). :param task: The task to add time data to. - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :param is_recurring: Whether or not the time should recur. :type is_recurring: bool :return: The newly created IfcTaskTime. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py index a69e22fa0f..35a022a9ea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py @@ -39,7 +39,7 @@ class Usecase: :param recurrence_pattern: The IfcRecurrencePattern to add the time period to. See ifcopenshell.api.sequence.assign_recurrence_pattern. - :type recurrence_pattern: ifcopenshell.entity_instance.entity_instance + :type recurrence_pattern: ifcopenshell.entity_instance :param start_time: The start time of the time period, in a format compatible with IfcTime, such as an ISO format time string or a datetime.time object. @@ -49,7 +49,7 @@ class Usecase: datetime.time object. :type end_time: str,datetime.time :return: The newly created IfcTimePeriod - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index 9df45247c5..373d628be6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -41,7 +41,7 @@ class Usecase: specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage. :return: The newly created IfcWorkCalendar - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py index 4e907a0ad5..f6fba71315 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py @@ -42,7 +42,7 @@ class Usecase: within the work plan are relevant. :type start_time: str,datetime.time :return: The newly created IfcWorkPlan - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index f50745471f..47e96a8fa3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -50,9 +50,9 @@ class Usecase: provided, the schedule will not be grouped in a work plan and would exist as a top level schedule in the project. This is not recommended. - :type work_plan: ifcopenshell.entity_instance.entity_instance,optional + :type work_plan: ifcopenshell.entity_instance,optional :return: The newly created IfcWorkSchedule - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py index 6afafad753..0d75914949 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py @@ -31,12 +31,12 @@ class Usecase: :param work_calendar: The IfcWorkCalendar to add the work or holiday time definition to. - :type work_calendar: ifcopenshell.entity_instance.entity_instance + :type work_calendar: ifcopenshell.entity_instance :param time_type: Either WorkingTimes or ExceptionTimes, depending on what you want to define. :type time_type: str :return: The newly created IfcWorkTime - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py index cc1abf7409..87f2882055 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py @@ -33,7 +33,7 @@ class Usecase: are allowed. :param rel_sequence: The IfcRelSequence to assign the lag time to. - :type rel_sequence: ifcopenshell.entity_instance.entity_instance + :type rel_sequence: ifcopenshell.entity_instance :param lag_value: An ISO standardised duration string. :type lag_value: str :param duration_type: Choose from WORKTIME for the associated @@ -43,7 +43,7 @@ class Usecase: is unclear. :type duration_type: str :return: The newly created IfcLagTime - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py index ea758b9182..5aa2210d42 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py @@ -61,12 +61,12 @@ class Usecase: :param relating_process: The IfcProcess (typically IfcTask) that the input, control, or resource is related to. - :type relating_process: ifcopenshell.entity_instance.entity_instance + :type relating_process: ifcopenshell.entity_instance :param related_object: The IfcProduct (for input), IfcCostItem (for control) or IfcConstructionResource (for resource). - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToProcess relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py index d8003227d1..3431a71f19 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py @@ -37,12 +37,12 @@ class Usecase: :param relating_product: The IfcProduct that was constructed as a result of the task. - :type relating_product: ifcopenshell.entity_instance.entity_instance + :type relating_product: ifcopenshell.entity_instance :param related_object: The IfcProcess (typically IfcTask) of the construction task. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: The newly created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py index a940e4f224..a3243f1057 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py @@ -60,11 +60,11 @@ class Usecase: :param parent: Either an IfcTaskTimeRecurring if you are defining a recurring schedule for a task, or IfcWorkTime if you are defining a recurring pattern for a workdays or holidays in a calendar. - :type parent: ifcopenshell.entity_instance.entity_instance + :type parent: ifcopenshell.entity_instance :param recurrence_type: One of the types of recurrences. :type recurrence_type: str :return: The newly created IfcRecurrencePattern - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py index 078f5b7f5c..e1c1100760 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py @@ -51,13 +51,13 @@ class Usecase: predecessor and successor tasks in the planning profession. :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance.entity_instance + :type relating_process: ifcopenshell.entity_instance :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance.entity_instance + :type related_process: ifcopenshell.entity_instance :param sequence_type: Choose from FINISH_START, FINISH_FINISH, START_START, or START_FINISH. :return: The newly created IfcRelSequence - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index b3573db5d5..a1eaed71be 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -29,11 +29,11 @@ class Usecase: :param work_schedule: The IfcWorkSchedule that will be assigned to the work plan. - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :param work_plan: The IfcWorkPlan for the schedule to be assigned to. - :type work_plan: ifcopenshell.entity_instance.entity_instance + :type work_plan: ifcopenshell.entity_instance :return: The IfcRelAggregates relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py index 671113f9f1..6698ab85a2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py @@ -36,7 +36,7 @@ class Usecase: then nothing happens. :param task: The IfcTask to calculate the duration for. - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py index a069bb4901..2a720b9fa1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py @@ -42,7 +42,7 @@ class Usecase: be equivalent to be Tuesday 8am, for instance. :param task: The start task to begin cascading from. - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py index 8f6e82c750..c0ebe4f72f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py @@ -37,9 +37,9 @@ class Usecase: * Same Resource Relationships :param work_schedule: The planned work_schedule to baseline - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :return: The baseline work_schedule - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: .. code:: python diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py index 195c5c71cd..91d5ed1b99 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py @@ -33,9 +33,9 @@ class Usecase: * The copy will have duplicated nested tasks :param task: The task to be duplicated - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :return: The duplicated task or the list of duplicated tasks if the latter has children - :rtype: ifcopenshell.entity_instance.entity_instance or list of ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance Example: .. code:: python diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py index 5f9a2dfcb4..f5779b058c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py @@ -28,7 +28,7 @@ class Usecase: IfcLagTime, consult the IFC documentation. :param lag_time: The IfcLagTime entity you want to edit - :type lag_time: ifcopenshell.entity_instance.entity_instance + :type lag_time: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py index f5eb567f6d..21292233ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -28,7 +28,7 @@ class Usecase: IfcRecurrencePattern, consult the IFC documentation. :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit - :type recurrence_pattern: ifcopenshell.entity_instance.entity_instance + :type recurrence_pattern: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py index 56c38d52ea..c563cb6990 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py @@ -28,7 +28,7 @@ class Usecase: IfcRelSequence, consult the IFC documentation. :param rel_sequence: The IfcRelSequence entity you want to edit - :type rel_sequence: ifcopenshell.entity_instance.entity_instance + :type rel_sequence: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py index d6ffdd5d17..cbdaa18de0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py @@ -25,7 +25,7 @@ class Usecase: IfcTask, consult the IFC documentation. :param task: The IfcTask entity you want to edit - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py index 68b5d59bbb..10bec7909c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py @@ -36,7 +36,7 @@ class Usecase: IfcTaskTime, consult the IFC documentation. :param task_time: The IfcTaskTime entity you want to edit - :type task_time: ifcopenshell.entity_instance.entity_instance + :type task_time: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py index cdfec38271..12ce1e15d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py @@ -25,7 +25,7 @@ class Usecase: IfcWorkCalendar, consult the IFC documentation. :param work_calendar: The IfcWorkCalendar entity you want to edit - :type work_calendar: ifcopenshell.entity_instance.entity_instance + :type work_calendar: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py index 39c426e2b8..669ef0193c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py @@ -27,7 +27,7 @@ class Usecase: IfcWorkPlan, consult the IFC documentation. :param work_plan: The IfcWorkPlan entity you want to edit - :type work_plan: ifcopenshell.entity_instance.entity_instance + :type work_plan: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py index e520b49419..cd7ca163b2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py @@ -27,7 +27,7 @@ class Usecase: IfcWorkSchedule, consult the IFC documentation. :param work_schedule: The IfcWorkSchedule entity you want to edit - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py index 4512789fdb..d62c3a5357 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -33,7 +33,7 @@ class Usecase: IfcWorkTime, consult the IFC documentation. :param work_time: The IfcWorkTime entity you want to edit - :type work_time: ifcopenshell.entity_instance.entity_instance + :type work_time: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py index 26ad4af7fa..eb8af300d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py @@ -27,12 +27,12 @@ class Usecase: utility module. :param relating_product: One of the products already output by the task. - :type relating_product: ifcopenshell.entity_instance.entity_instance + :type relating_product: ifcopenshell.entity_instance :param related_object: The IfcTask that you want to get all the related products for. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: A set of IfcProducts output by the IfcTask. - :rtype: set[ifcopenshell.entity_instance.entity_instance] + :rtype: set[ifcopenshell.entity_instance] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py index 4039c53c57..da07337f7b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py @@ -36,7 +36,7 @@ class Usecase: error. :param work_schedule: The IfcWorkSchedule to perform the calculation on. - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py index 870efa1876..6b7fac4f75 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py @@ -29,7 +29,7 @@ class Usecase: sequences or controls are also removed. :param task: The IfcTask to remove. - :type task: ifcopenshell.entity_instance.entity_instance + :type task: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py index 925c6289ff..32effdf4ac 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py @@ -24,7 +24,7 @@ class Usecase: """Removes a time period :param time_period: The IfcTimePeriod to remove. - :type time_period: ifcopenshell.entity_instance.entity_instance + :type time_period: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index 242165c201..e233bef26d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -28,7 +28,7 @@ class Usecase: calendar. :param work_calendar: The IfcWorkCalendar to remove - :type work_calendar: ifcopenshell.entity_instance.entity_instance + :type work_calendar: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py index 905a7b9b8a..bbd631829d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py @@ -28,7 +28,7 @@ class Usecase: removed. :param work_plan: The IfcWorkPlan to remove. - :type work_plan: ifcopenshell.entity_instance.entity_instance + :type work_plan: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py index b8bbcd4616..66b69c8804 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py @@ -28,7 +28,7 @@ class Usecase: All tasks in the work schedule are also removed recursively. :param work_schedule: The IfcWorkSchedule to remove. - :type work_schedule: ifcopenshell.entity_instance.entity_instance + :type work_schedule: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py index 6f8e1d13eb..3898e3655a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py @@ -22,7 +22,7 @@ class Usecase: """Removes a work time :param work_time: The IfcWorkTime to remove. - :type work_time: ifcopenshell.entity_instance.entity_instance + :type work_time: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py index 94a5fe16fb..cca278da44 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py @@ -26,7 +26,7 @@ class Usecase: The schedule is cascaded afterwards. :param rel_sequence: The sequence to remove the lag time from. - :type rel_sequence: ifcopenshell.entity_instance.entity_instance + :type rel_sequence: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py index 2d7a5fb10f..dfc12068d3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py @@ -28,9 +28,9 @@ class Usecase: See ifcopenshell.api.sequence.assign_process for details. :param relating_process: The IfcTask in the relationship. - :type relating_process: ifcopenshell.entity_instance.entity_instance + :type relating_process: ifcopenshell.entity_instance :param related_object: The related object. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py index a4dcfd6968..31d9edb0e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py @@ -28,9 +28,9 @@ class Usecase: See ifcopenshell.api.sequence.assign_product for details. :param relating_product: The IfcProduct in the relationship. - :type relating_product: ifcopenshell.entity_instance.entity_instance + :type relating_product: ifcopenshell.entity_instance :param related_object: The IfcTask in the relationship. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py index 7a13495289..fc74c69a95 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py @@ -25,7 +25,7 @@ class Usecase: you remove it, be sure to clean up after yourself. :param recurrence_pattern: The IfcRecurrencePattern to remove. - :type recurrence_pattern: ifcopenshell.entity_instance.entity_instance + :type recurrence_pattern: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py index 85afb4cd23..c7286909bd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py @@ -26,9 +26,9 @@ class Usecase: """Removes a sequence relationship between tasks :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance.entity_instance + :type relating_process: ifcopenshell.entity_instance :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance.entity_instance + :type related_process: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py index 70dacdce00..9edf7ccba8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py @@ -68,13 +68,13 @@ class Usecase: previous aggregation, containment, or nesting relationships it may have. :param products: A list of physical IfcElements existing in the space. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param relating_structure: The IfcSpatialStructureElement element, such as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element exists in. :return: The IfcRelContainedInSpatialStructure relationship instance or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index 00cb94d8b3..6902018b46 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -31,7 +31,7 @@ class Usecase: """Dereferences a list of products and space :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param relating_structure: The IfcSpatialStructureElement element, such as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element exists in. diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index 6d8915828a..32ef580b96 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -47,11 +47,11 @@ class Usecase: spaces simultaneously. :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param relating_structure: The IfcSpatialStructureElement element, such as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element exists in. - :type relating_structure: ifcopenshell.entity_instance.entity_instance + :type relating_structure: ifcopenshell.entity_instance :return: The IfcRelReferencedInSpatialStructure relationship instance or `None` if `products` was an empty list. :rtype: Union[ifcopenshell.entity_instance, None] diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py index f7c831b9fd..d1418d3be5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py @@ -26,7 +26,7 @@ class Usecase: """Unassigns a container from products. :param product: A list of IfcProducts to remove the containment from. - :type product: list[ifcopenshell.entity_instance.entity_instance] + :type product: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py index 9bf5888622..faf1daf366 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py @@ -51,12 +51,12 @@ class Usecase: :type global_or_local: str :param applied_load: The IfcStructuralLoad that is applied in this activity. - :type applied_load: ifcopenshell.entity_instance.entity_instance + :type applied_load: ifcopenshell.entity_instance :param structural_member: The IfcStructuralMember that the load is applied to. - :type structural_member: ifcopenshell.entity_instance.entity_instance + :type structural_member: ifcopenshell.entity_instance :return: The newly created entity based on the ifc_class - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py index 97e85464b8..39181f9a4c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py @@ -31,7 +31,7 @@ class Usecase: A 3D analytical model is assumed. :return: The newly created IfcStructuralAnalysisModel - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py index 701274f992..1cd9dfef9f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py @@ -31,13 +31,13 @@ class Usecase: condition to. This will determine the type of condition that is created. If no connection is supplied, an orphan boundary condition will be created using the ifc_class that you specify. - :type connection: ifcopenshell.entity_instance.entity_instance,optional + :type connection: ifcopenshell.entity_instance,optional :param ifc_class: The class of IfcBoundaryCondition to create, only relevant if you do not specify a connection and want to create an orphaned boundary condition. :type ifc_class: str,optional :return: The newly created IfcBoundaryCondition - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py index c7d73bef6f..6d51d7dc22 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py @@ -34,7 +34,7 @@ class Usecase: :type ifc_class: str :return: The newly created load entity, depending on the ifc_class specified. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py index 6cab9bcb45..afc4e676db 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py @@ -35,7 +35,7 @@ class Usecase: IfcActionSourceTypeEnum in the IFC documentation. :type action_source: str :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py index 2758b1d589..497977fe6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py @@ -35,7 +35,7 @@ class Usecase: IfcActionSourceTypeEnum in the IFC documentation. :type action_source: str :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py index 2cb2b7a722..eda5fc96c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py @@ -26,12 +26,12 @@ class Usecase: :param relating_structural_member: The IfcStructuralMember to have a connection added to it. - :type relating_structural_member: ifcopenshell.entity_instance.entity_instance + :type relating_structural_member: ifcopenshell.entity_instance :param related_structural_connection: The IfcStructuralConnection to add to the IfcStructuralMember. - :type related_structural_connection: ifcopenshell.entity_instance.entity_instance + :type related_structural_connection: ifcopenshell.entity_instance :return: The IfcRelConnectsStructuralMember relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py index b0db0356be..61f771c982 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py @@ -25,12 +25,12 @@ class Usecase: """Assigns a load or structural member to an analysis model :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param structural_analysis_model: The IfcStructuralAnalysisModel that the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance.entity_instance + :type structural_analysis_model: ifcopenshell.entity_instance :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py index 354196946d..39c46f6fe7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py @@ -25,7 +25,7 @@ class Usecase: IfcStructuralAnalysisModel, consult the IFC documentation. :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit - :type structural_analysis_model: ifcopenshell.entity_instance.entity_instance + :type structural_analysis_model: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py index d7822f80cf..e6814c5242 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py @@ -25,7 +25,7 @@ class Usecase: IfcBoundaryCondition, consult the IFC documentation. :param condition: The IfcBoundaryCondition entity you want to edit - :type condition: ifcopenshell.entity_instance.entity_instance + :type condition: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py index 39ab992b05..a66bbb989e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py @@ -22,7 +22,7 @@ class Usecase: """Edits the coordinate system of a structural connection :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance.entity_instance + :type structural_item: ifcopenshell.entity_instance :param axis: The unit Z axis vector defined as a list of 3 floats. Defaults to [0., 0., 1.]. :type axis: list[float] diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py index e255ca6e2b..ec4b163aca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py @@ -22,7 +22,7 @@ class Usecase: """Edits the coordinate system of a structural connection :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance.entity_instance + :type structural_item: ifcopenshell.entity_instance :param axis: The unit Z axis vector defined as a list of 3 floats. Defaults to [0., 0., 1.]. :type axis: list[float] diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py index 2b13795b17..3adba0ade9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py @@ -25,7 +25,7 @@ class Usecase: IfcStructuralLoad, consult the IFC documentation. :param structural_load: The IfcStructuralLoad entity you want to edit - :type structural_load: ifcopenshell.entity_instance.entity_instance + :type structural_load: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py index 0d0985accc..cffce454bf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py @@ -25,7 +25,7 @@ class Usecase: IfcStructuralLoadCase, consult the IFC documentation. :param load_case: The IfcStructuralLoadCase entity you want to edit - :type load_case: ifcopenshell.entity_instance.entity_instance + :type load_case: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py index b4148687aa..4ebff6cc3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py @@ -28,7 +28,7 @@ class Usecase: :param structural_analysis_model: The IfcStructuralAnalysisModel to remove. - :type structural_analysis_model: ifcopenshell.entity_instance.entity_instance + :type structural_analysis_model: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py index f0e7b0b33f..02cfb79e3c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py @@ -23,9 +23,9 @@ class Usecase: :param connection: The IfcStructuralConnection to remove the condition from. If omitted, it is assumed to be an orphaned condition. - :type connection: ifcopenshell.entity_instance.entity_instance,optional + :type connection: ifcopenshell.entity_instance,optional :param boundary_condition: The IfcBoundaryCondition to remove. - :type boundary_condition: ifcopenshell.entity_instance.entity_instance + :type boundary_condition: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py index a09618575f..28ce9fc4c9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py @@ -28,7 +28,7 @@ class Usecase: The condition and the member itself is preserved. :param relation: The IfcRelConnectsStructuralMember to remove. - :type relation: ifcopenshell.entity_instance.entity_instance + :type relation: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py index 1bdecdae4e..55b83a7f1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py @@ -22,7 +22,7 @@ class Usecase: """Removes a structural load :param structural_load: The IfcStructuralLoad to remove. - :type structural_load: ifcopenshell.entity_instance.entity_instance + :type structural_load: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py index c9aa1b2c5d..e331309239 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py @@ -26,7 +26,7 @@ class Usecase: """Removes a structural load case :param load_case: The IfcStructuralLoadCase to remove. - :type load_case: ifcopenshell.entity_instance.entity_instance + :type load_case: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py index 937af21bca..93500aba1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py @@ -26,7 +26,7 @@ class Usecase: """Removes a structural load group :param load_group: The IfcStructuralLoadGroup to remove. - :type load_group: ifcopenshell.entity_instance.entity_instance + :type load_group: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py index 0dd7bf8ca4..5a86a6a9f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py @@ -26,10 +26,10 @@ class Usecase: """Removes a relationship between a structural element and the analysis model :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :param structural_analysis_model: The IfcStructuralAnalysisModel that the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance.entity_instance + :type structural_analysis_model: ifcopenshell.entity_instance :return: None :rtype: None """ diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py index 575548287b..650043039f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py @@ -46,7 +46,7 @@ class Usecase: :type ifc_class: str :return: The newly created style element, based on the provided ifc_class. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py index f3d97946f8..32064811f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py @@ -68,7 +68,7 @@ class Usecase: :param style: The IfcSurfaceStyle you want to add to presentation item to. See ifcopenshell.api.style.add_style. - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :param ifc_class: Choose from IfcSurfaceStyleShading, IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or @@ -78,7 +78,7 @@ class Usecase: :type attributes: dict, optional :return: The newly created presentation item based on the provided ifc_class. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py index 70a0da1e45..88aabfe801 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py @@ -31,7 +31,7 @@ class Usecase: :param uv_maps: A list of IfcIndexedTextureMap for any IfcTessellatedFaceSets that the representation has, obtained from the HasTextures attribute. - :type uv_maps: list[ifcopenshell.entity_instance.entity_instance] + :type uv_maps: list[ifcopenshell.entity_instance] :param textures: A list of dictionaries containing: 1. Attributes to create IfcImageTexture. @@ -47,7 +47,7 @@ class Usecase: based on camera position) :type textures: list[dict] :return: A list of IfcImageTexture - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ # TODO: This usecase currently depends on Blender's data model self.file = file diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py index 11a82097a6..06d3a6339a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py @@ -32,14 +32,14 @@ class Usecase: to materials. This API function provides that capability. :param material: The IfcMaterial which you want to assign the style to. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that you want to assign to the material. This will then be applied to all objects that have that material. - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :param context: The IfcGeometricRepresentationSubContext at which this style should be used. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance.entity_instance + :type context: ifcopenshell.entity_instance :param should_use_presentation_style_assignment: This is a technical detail to accomodate a bug in Revit. This should always be left as the default of False, unless you are finding that colours aren't diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py index 860abc83db..c6236c8a4a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py @@ -42,12 +42,12 @@ class Usecase: :param shape_representation: The IfcShapeRepresentation of the object that you want to assign styles to. This implicitly defines the context at which the styles should be used. - :type shape_representation: ifcopenshell.entity_instance.entity_instance + :type shape_representation: ifcopenshell.entity_instance :param styles: A list of presentation styles, typically IfcSurfaceStyle. The number of items in the list should correlate with the number of items in the shape_representation's Items attribute. If you have more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance.entity_instance] + :type styles: list[ifcopenshell.entity_instance] :param replace_previous_same_type_style: Remove previously assigned styles of the same type as currently assign style`. Defaults to `True`. :type replace_previous_same_type_style: bool @@ -58,7 +58,7 @@ class Usecase: that this is no longer a valid IFC. Blame Autodesk. :type should_use_presentation_style_assignment: bool :return: List of created IfcStyledItems - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py index 7c928e294f..877d0f89c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py @@ -25,7 +25,7 @@ class Usecase: IfcPresentationStyle, consult the IFC documentation. :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py index 9031db5dd5..20c1002fdf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py @@ -33,7 +33,7 @@ class Usecase: example below. :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py index c9c9c76a95..40692982bb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py @@ -26,7 +26,7 @@ class Usecase: All of the presentation items of the style will also be removed. :param style: The IfcPresentationStyle to remove. - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py index 4381f91a4b..ab061e182c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py @@ -25,7 +25,7 @@ class Usecase: removes the representation but not the underlying styles. :param representation: The IfcStyledRepresentation to remove. - :type representation: ifcopenshell.entity_instance.entity_instance + :type representation: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py index 5e17b2b06d..ce214dbf21 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py @@ -25,7 +25,7 @@ class Usecase: """Removes a presentation item from a presentation style :param style: The IfcPresentationItem to remove. - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py index 674e5eb7f0..f1e2e7e85b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py @@ -27,14 +27,14 @@ class Usecase: This does the inverse of assign_material_style. :param material: The IfcMaterial which you want to unassign the style from. - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that you want to unassign from material. This will then be applied to all objects that have that material. - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :param context: The IfcGeometricRepresentationSubContext at which this style should be unassigned. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance.entity_instance + :type context: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py index 6211a9f4dd..83f60fe3d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py @@ -25,12 +25,12 @@ class Usecase: :param shape_representation: The IfcShapeRepresentation of the object that you want to unassign styles from. - :type shape_representation: ifcopenshell.entity_instance.entity_instance + :type shape_representation: ifcopenshell.entity_instance :param styles: A list of presentation styles, typically IfcSurfaceStyle. The number of items in the list should correlate with the number of items in the shape_representation's Items attribute. If you have more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance.entity_instance] + :type styles: list[ifcopenshell.entity_instance] :param should_use_presentation_style_assignment: This is a technical detail to accomodate a bug in Revit. This should always be left as the default of False, unless you are finding that colours aren't diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py index d0512c3cd3..f792ecc2fa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py @@ -35,9 +35,9 @@ class Usecase: :param element: The IfcDistributionElement you want to add a distribution port to. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The newly created IfcDistributionPort - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py index 0fdf9992d7..26c8cfe8fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py @@ -36,7 +36,7 @@ class Usecase: IfcSystem. :type ifc_class: str :return: The newly created IfcSystem. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py index 4517a42421..aae80ab6eb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py @@ -29,12 +29,12 @@ class Usecase: :param related_flow_control: IfcDistributionControlElement which may be used to impart control on the flow element - :type related_flow_control: ifcopenshell.entity_instance.entity_instance + :type related_flow_control: ifcopenshell.entity_instance :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed - :type relating_flow_element: ifcopenshell.entity_instance.entity_instance + :type relating_flow_element: ifcopenshell.entity_instance :return: Matching or newly created IfcRelFlowControlElements. If control is already assigned to some other element method will return None. - :rtype: ifcopenshell.entity_instance.entity_instance, None + :rtype: ifcopenshell.entity_instance, None Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py index 91a1ce91be..728a935395 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py @@ -30,12 +30,12 @@ class Usecase: it may be useful when patching up models. :param element: The IfcDistributionElement to assign the port to. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param port: The IfcDistributionPort you want to assign. - :type port: ifcopenshell.entity_instance.entity_instance + :type port: ifcopenshell.entity_instance :return: The IfcRelNests relationship, or the IfcRelConnectsPortToElement for IFC2X3. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py index 3df49d776d..20f5a8519f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py @@ -33,12 +33,12 @@ class Usecase: Note that it is not necessary to assign distribution ports to a system. :param products: The list of IfcDistributionElements to assign to the system. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param system: The IfcSystem you want to assign the element to. - :type system: ifcopenshell.entity_instance.entity_instance + :type system: ifcopenshell.entity_instance :return: The IfcRelAssignsToGroup relationship or `None` if `products` was empty list. - :rtype: [ifcopenshell.entity_instance.entity_instance, None] + :rtype: [ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py index 4d0b75e8a6..7d23dde1a7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py @@ -45,9 +45,9 @@ class Usecase: and implicit connectivity is preferred for early phase design. :param port1: The port of the first distribution element to connect. - :type port1: ifcopenshell.entity_instance.entity_instance + :type port1: ifcopenshell.entity_instance :param port2: The port of the second distribution element to connect. - :type port2: ifcopenshell.entity_instance.entity_instance + :type port2: ifcopenshell.entity_instance :param direction: The directionality of distribution flow through the port connection. NOTDEFINED means that the direction has not yet been determined. This is useful during preliminary system design. @@ -61,7 +61,7 @@ class Usecase: connectivity is made, such as a segment or fitting. This is only to be used for implicit port connectivity where the segments and fittings are less important. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py index 12d48af4b2..071074e9e7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py @@ -29,7 +29,7 @@ class Usecase: needed to be specified. :param port: The IfcDistributionPort to disconnect. - :type port: ifcopenshell.entity_instance.entity_instance + :type port: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py index fefdfa0f58..315c04ccd5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py @@ -25,7 +25,7 @@ class Usecase: IfcSystem, consult the IFC documentation. :param system: The IfcSystem entity you want to edit - :type system: ifcopenshell.entity_instance.entity_instance + :type system: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py index f4cfe0e2d3..f331a5f3e3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py @@ -28,7 +28,7 @@ class Usecase: All the distribution elements within the system are retained. :param system: The IfcSystem to remove. - :type system: ifcopenshell.entity_instance.entity_instance + :type system: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py index 3ed1fe4cd5..04eda27f83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py @@ -27,12 +27,12 @@ class Usecase: :param related_flow_control: IfcDistributionControlElement controling the flow element - :type related_flow_control: ifcopenshell.entity_instance.entity_instance + :type related_flow_control: ifcopenshell.entity_instance :param relating_flow_element: The IfcDistributionFlowElement that is being controlled - :type relating_flow_element: ifcopenshell.entity_instance.entity_instance + :type relating_flow_element: ifcopenshell.entity_instance :return: If the control still is related to other objects, the IfcRelFlowControlElements is returned, otherwise None. - :rtype: ifcopenshell.entity_instance.entity_instance, None + :rtype: ifcopenshell.entity_instance, None Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py index 81c8751945..e9d82722aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py @@ -29,9 +29,9 @@ class Usecase: port for cleaning or patchin purposes. :param element: The IfcDistributionElement to unassign the port from. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param port: The IfcDistributionPort you want to unassign. - :type port: ifcopenshell.entity_instance.entity_instance + :type port: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py index b402407038..fbc3dd854b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py @@ -31,9 +31,9 @@ class Usecase: """Unassigns list of products from a system :param products: The list of IfcDistributionElements to unassign from the system. - :type products: list[ifcopenshell.entity_instance.entity_instance] + :type products: list[ifcopenshell.entity_instance] :param system: The IfcSystem you want to unassign the element from. - :type system: ifcopenshell.entity_instance.entity_instance + :type system: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index 68da4c0da5..b8d7cf357a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -91,9 +91,9 @@ class Usecase: ambiguous, unknown or are so bespoke as to have no logical type. :param related_objects: The IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance.entity_instance] + :type related_objects: list[ifcopenshell.entity_instance] :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance.entity_instance + :type relating_type: ifcopenshell.entity_instance :param should_map_representations: If a type has a representation map, IFC requires all occurrences to map those representations. Some IFC vendors might disobey this, or you might want to handle it @@ -102,7 +102,7 @@ class Usecase: :type should_map_representations: bool :return: The IfcRelDefinesByType relationship or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py index 2e8d562fbf..0a05de4118 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py @@ -28,11 +28,11 @@ class Usecase: ifcopenshell.util.element.get_types instead. :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance.entity_instance + :type relating_type: ifcopenshell.entity_instance :return: A list of occurrences of the type. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ self.file = file self.settings = { diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py index 180e163477..064fb148bb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py @@ -35,9 +35,9 @@ class Usecase: be used to ensure consistency of the occurrence's representations. :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance.entity_instance + :type related_object: ifcopenshell.entity_instance :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance.entity_instance + :type relating_type: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py index c376a58971..a629100477 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py @@ -29,7 +29,7 @@ class Usecase: and material usages associated with the previously assigned type. :param related_objects: List of IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance.entity_instance] + :type related_objects: list[ifcopenshell.entity_instance] :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py index 631daaa99d..a0d705a94b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py @@ -43,7 +43,7 @@ class Usecase: recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0). :type dimensions: list[int] :return: The new IfcContextDependentUnit - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py index 3102a482fa..20b96d3298 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py @@ -45,7 +45,7 @@ class Usecase: :type conversion_offset: float, optional :return: The new IfcConversionBasedUnit or IfcConversionBasedUnitWithOffset - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py index fb15af4cd5..f15b18ab91 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py @@ -28,7 +28,7 @@ class Usecase: :param currency: The currency code :type currency: str :return: The newly created IfcMonetaryUnit - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py index bd0cd26e39..7eb8019632 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py @@ -44,7 +44,7 @@ class Usecase: prefix. :type prefix: str,optional :return: The newly created IfcSIUnit - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index bca68744d7..67305295bd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -44,9 +44,9 @@ class Usecase: :param units: A list of units to assign as project defaults. See ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit, and unit.add_monetary_unit for information on how to create units. - :type units: list[ifcopenshell.entity_instance.entity_instance],optional + :type units: list[ifcopenshell.entity_instance],optional :return: The IfcUnitAssignment element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py index d3a3c47fe5..636430159c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py @@ -25,7 +25,7 @@ class Usecase: IfcDerivedUnit, consult the IFC documentation. :param unit: The IfcDerivedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance.entity_instance + :type unit: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py index 9f27e20d96..aee4b89305 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py @@ -25,7 +25,7 @@ class Usecase: IfcMonetaryUnit, consult the IFC documentation. :param unit: The IfcMonetaryUnit entity you want to edit - :type unit: ifcopenshell.entity_instance.entity_instance + :type unit: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py index c4652ba55a..da4ff5290f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py @@ -28,7 +28,7 @@ class Usecase: IfcNamedUnit, consult the IFC documentation. :param unit: The IfcNamedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance.entity_instance + :type unit: ifcopenshell.entity_instance :param attributes: a dictionary of attribute names and values. :type attributes: dict, optional :return: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py index 361cfbd0c3..ba9aff862b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py @@ -28,7 +28,7 @@ class Usecase: defined quantities in the model completely lose their meaning. :param unit: The unit element to remove - :type unit: ifcopenshell.entity_instance.entity_instance + :type unit: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py index fb1dcafb02..2da27bd08c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py @@ -24,7 +24,7 @@ class Usecase: """Unassigns units as default units for the project :param units: A list of units to assign as project defaults. - :type units: list[ifcopenshell.entity_instance.entity_instance],optional + :type units: list[ifcopenshell.entity_instance],optional :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py index b457ab5d3a..a2867450f6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py @@ -31,11 +31,11 @@ class Usecase: filled. :param opening: The IfcOpeningElement to fill with the element. - :type opening: ifcopenshell.entity_instance.entity_instance + :type opening: ifcopenshell.entity_instance :param element: The IfcElement to be inserted into the opening. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The new IfcRelFillsElement relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py index 3a260a08f7..142eaedd87 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py @@ -51,11 +51,11 @@ class Usecase: booleaned or be part of the shape of the object). :param opening: The IfcOpeningElement to cut out the element. - :type opening: ifcopenshell.entity_instance.entity_instance + :type opening: ifcopenshell.entity_instance :param element: The IfcElement to insert the opening into. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The new IfcRelVoidsElement relationship - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py index d277960a5f..6d5ab79752 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py @@ -29,7 +29,7 @@ class Usecase: fills the opening. :param element: The element filling an opening. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: None :rtype: None diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py index 3735d2e735..5ffba93e25 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py @@ -29,7 +29,7 @@ class Usecase: removed, the opening is also removed. :param opening: The IfcOpeningElement to remove. - :type opening: ifcopenshell.entity_instance.entity_instance + :type opening: ifcopenshell.entity_instance :return: None :rtype: None From 34bbc8f1ead3bcd22b2b364ffdb14770812cbec9 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Sun, 5 May 2024 19:20:13 -0500 Subject: [PATCH 33/62] for 'Add Fitting' changed the hot key from Shift+F to Shift+Y. per https://community.osarch.org/discussion/2124/ui-discussion-around-add-fitting-and-add-bend Also added Shift+F (bim.flip_object) to all profile-based objects --- .../blenderbim/bim/module/model/workspace.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index c1c62f95ce..324bf42e29 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -378,6 +378,7 @@ class BimToolUI: op.depth = cls.props.extrusion_depth add_layout_hotkey_operator(cls.layout, "Extend", "S_E", "") + add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__) if AuthoringData.data["active_class"] in ( "IfcCableCarrierSegment", @@ -385,8 +386,8 @@ class BimToolUI: "IfcDuctSegment", "IfcPipeSegment", ): - add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_F", "") + add_layout_hotkey_operator(cls.layout, "Add Fitting", "S_Y", "") if context.region.type != "TOOL_HEADER": cls.layout.operator("bim.mep_add_bend") cls.layout.operator("bim.mep_add_transition") @@ -394,7 +395,6 @@ class BimToolUI: else: add_layout_hotkey_operator(cls.layout, "Edit Axis", "A_E", "") - add_layout_hotkey_operator(cls.layout, "Flip", "S_F", bpy.ops.bim.flip_object.__doc__) add_layout_hotkey_operator(cls.layout, "Butt", "S_T", "") add_layout_hotkey_operator(cls.layout, "Mitre", "S_Y", "") add_layout_hotkey_operator(cls.layout, "Rotate 90", "S_R", bpy.ops.bim.rotate_90.__doc__) @@ -719,10 +719,9 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): bpy.ops.bim.flip_wall() elif self.active_class in ("IfcWindow", "IfcWindowStandardCase", "IfcDoor", "IfcDoorStandardCase"): bpy.ops.bim.flip_fill() - elif self.active_class in ("IfcBeam", "IfcColumn"): + elif self.active_material_usage == "PROFILE": bpy.ops.bim.flip_object(flip_local_axes="XZ") - elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"): - bpy.ops.bim.fit_flow_segments() + def hotkey_S_G(self): obj = bpy.context.active_object @@ -808,9 +807,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): return if self.active_material_usage == "LAYER2": bpy.ops.bim.join_wall(join_type="V") + elif self.active_class in ("IfcDuctSegment", "IfcPipeSegment", "IfcCableCarrierSegment", "IfcCableSegment"): + bpy.ops.bim.fit_flow_segments() elif self.active_material_usage == "PROFILE": bpy.ops.bim.extend_profile(join_type="V") + def hotkey_S_B(self): bpy.ops.bim.add_boundary() From 10f894e2ea2fac53431842e2be69680440c717c9 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Sun, 5 May 2024 20:46:07 -0500 Subject: [PATCH 34/62] Find the "Types" Collection regardless of the IfcProject.Name --- .../blenderbim/bim/module/type/operator.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/type/operator.py b/src/blenderbim/blenderbim/bim/module/type/operator.py index f5f08c35b3..820c197eb9 100644 --- a/src/blenderbim/blenderbim/bim/module/type/operator.py +++ b/src/blenderbim/blenderbim/bim/module/type/operator.py @@ -157,8 +157,9 @@ class SelectType(bpy.types.Operator): selected_objs.append(active_obj) #update selected_objs so the active_obj is at the end of the list last_relating_type_obj = None + types_collection_in_view_layer = self.find_collection_in_ifcproject(context, collection_name = "Types") + types_collection_in_view_layer.hide_viewport = False types_collection = bpy.data.collections.get("Types") - context.view_layer.layer_collection.children['IfcProject/My Project'].children["Types"].hide_viewport = False for type_obj in types_collection.objects: type_obj.hide_set(True) for obj in selected_objs: @@ -175,9 +176,21 @@ class SelectType(bpy.types.Operator): obj.select_set(False) context.view_layer.objects.active = last_relating_type_obj #makes the active_obj's type the active object - + return {"FINISHED"} + def find_collection_in_ifcproject(self, context, collection_name): + + ifc_project_collection = None + for child in context.view_layer.layer_collection.children: + if "IfcProject" in child.name: + ifc_project_collection = child + break + + if ifc_project_collection: + collection_in_view_layer = ifc_project_collection.children.get(collection_name) + return collection_in_view_layer + class SelectSimilarType(bpy.types.Operator): bl_idname = "bim.select_similar_type" From d11ec671290ccdd6c3022925bd817e91c857b8d1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 6 May 2024 14:35:39 +1000 Subject: [PATCH 35/62] Generate functions for all API usecases for better static code features. See #2693. --- .../ifcopenshell/api/aggregate/__init__.py | 3 + .../api/aggregate/assign_object.py | 236 ++++++----- .../api/aggregate/unassign_object.py | 93 +++-- .../ifcopenshell/api/attribute/__init__.py | 2 + .../api/attribute/edit_attributes.py | 95 ++--- .../ifcopenshell/api/boundary/__init__.py | 5 + .../boundary/assign_connection_geometry.py | 134 ++++--- .../api/boundary/copy_boundary.py | 39 +- .../api/boundary/edit_attributes.py | 82 ++-- .../api/boundary/remove_boundary.py | 51 ++- .../api/classification/__init__.py | 7 + .../api/classification/add_classification.py | 125 +++--- .../api/classification/add_reference.py | 222 +++++------ .../api/classification/edit_classification.py | 43 +- .../api/classification/edit_reference.py | 43 +- .../classification/remove_classification.py | 49 +-- .../api/classification/remove_reference.py | 175 ++++----- .../ifcopenshell/api/constraint/__init__.py | 10 + .../ifcopenshell/api/constraint/add_metric.py | 67 ++-- .../api/constraint/add_metric_reference.py | 46 ++- .../api/constraint/add_objective.py | 47 ++- .../api/constraint/assign_constraint.py | 66 ++-- .../api/constraint/edit_metric.py | 45 +-- .../api/constraint/edit_objective.py | 41 +- .../api/constraint/remove_constraint.py | 51 ++- .../api/constraint/remove_metric.py | 51 +-- .../api/constraint/unassign_constraint.py | 50 +-- .../ifcopenshell/api/context/__init__.py | 4 + .../ifcopenshell/api/context/add_context.py | 325 +++++++-------- .../ifcopenshell/api/context/edit_context.py | 51 ++- .../api/context/remove_context.py | 73 ++-- .../ifcopenshell/api/control/__init__.py | 3 + .../api/control/assign_control.py | 142 ++++--- .../api/control/unassign_control.py | 85 ++-- .../ifcopenshell/api/cost/__init__.py | 20 + .../ifcopenshell/api/cost/add_cost_item.py | 83 ++-- .../api/cost/add_cost_item_quantity.py | 113 +++--- .../api/cost/add_cost_schedule.py | 71 ++-- .../ifcopenshell/api/cost/add_cost_value.py | 151 ++++--- .../api/cost/assign_cost_item_quantity.py | 141 ++++--- .../api/cost/assign_cost_value.py | 93 +++-- .../calculate_cost_item_resource_value.py | 163 ++++---- .../ifcopenshell/api/cost/copy_cost_item.py | 59 +-- .../api/cost/copy_cost_item_values.py | 65 ++- .../ifcopenshell/api/cost/edit_cost_item.py | 41 +- .../api/cost/edit_cost_item_quantity.py | 55 ++- .../api/cost/edit_cost_schedule.py | 41 +- .../ifcopenshell/api/cost/edit_cost_value.py | 73 ++-- .../api/cost/edit_cost_value_formula.py | 63 +-- .../ifcopenshell/api/cost/remove_cost_item.py | 71 ++-- .../api/cost/remove_cost_item_quantity.py | 59 ++- .../api/cost/remove_cost_schedule.py | 59 ++- .../api/cost/remove_cost_value.py | 77 ++-- .../api/cost/unassign_cost_item_quantity.py | 101 ++--- .../ifcopenshell/api/document/__init__.py | 9 + .../api/document/add_information.py | 107 +++-- .../api/document/add_reference.py | 95 +++-- .../api/document/assign_document.py | 148 ++++--- .../api/document/edit_information.py | 54 ++- .../api/document/edit_reference.py | 60 ++- .../api/document/remove_information.py | 63 ++- .../api/document/remove_reference.py | 43 +- .../api/document/unassign_document.py | 108 +++-- .../ifcopenshell/api/drawing/__init__.py | 4 + .../api/drawing/assign_product.py | 151 ++++--- .../api/drawing/edit_text_literal.py | 41 +- .../api/drawing/unassign_product.py | 85 ++-- .../ifcopenshell/api/geometry/__init__.py | 27 ++ .../api/geometry/add_axis_representation.py | 119 +++--- .../ifcopenshell/api/geometry/add_boolean.py | 37 +- .../api/geometry/add_door_representation.py | 145 ++++--- .../geometry/add_footprint_representation.py | 29 +- .../api/geometry/add_mesh_representation.py | 39 +- .../geometry/add_profile_representation.py | 33 +- .../geometry/add_railing_representation.py | 67 ++-- .../api/geometry/add_representation.py | 73 ++-- .../api/geometry/add_slab_representation.py | 29 +- .../api/geometry/add_wall_representation.py | 39 +- .../api/geometry/add_window_representation.py | 143 ++++--- .../api/geometry/assign_representation.py | 15 +- .../api/geometry/connect_element.py | 67 ++-- .../ifcopenshell/api/geometry/connect_path.py | 131 +++---- .../api/geometry/create_2pt_wall.py | 37 +- .../api/geometry/disconnect_element.py | 55 ++- .../api/geometry/disconnect_path.py | 63 ++- .../api/geometry/edit_object_placement.py | 36 +- .../api/geometry/map_representation.py | 17 +- .../api/geometry/remove_boolean.py | 15 +- .../api/geometry/remove_representation.py | 101 +++-- .../api/geometry/unassign_representation.py | 15 +- .../ifcopenshell/api/georeference/__init__.py | 4 + .../api/georeference/add_georeferencing.py | 73 ++-- .../api/georeference/edit_georeferencing.py | 143 +++---- .../api/georeference/remove_georeferencing.py | 39 +- .../ifcopenshell/api/grid/__init__.py | 4 + .../api/grid/create_axis_curve.py | 81 ++-- .../ifcopenshell/api/grid/create_grid_axis.py | 113 +++--- .../ifcopenshell/api/grid/remove_grid_axis.py | 51 ++- .../ifcopenshell/api/group/__init__.py | 7 + .../ifcopenshell/api/group/add_group.py | 67 ++-- .../ifcopenshell/api/group/assign_group.py | 87 ++-- .../ifcopenshell/api/group/edit_group.py | 41 +- .../ifcopenshell/api/group/remove_group.py | 85 ++-- .../ifcopenshell/api/group/unassign_group.py | 75 ++-- .../api/group/update_group_products.py | 79 ++-- .../ifcopenshell/api/layer/__init__.py | 6 + .../ifcopenshell/api/layer/add_layer.py | 43 +- .../ifcopenshell/api/layer/assign_layer.py | 101 +++-- .../ifcopenshell/api/layer/edit_layer.py | 41 +- .../ifcopenshell/api/layer/remove_layer.py | 33 +- .../ifcopenshell/api/layer/unassign_layer.py | 105 +++-- .../ifcopenshell/api/library/__init__.py | 9 + .../ifcopenshell/api/library/add_library.py | 69 ++-- .../ifcopenshell/api/library/add_reference.py | 71 ++-- .../api/library/assign_reference.py | 123 +++--- .../ifcopenshell/api/library/edit_library.py | 41 +- .../api/library/edit_reference.py | 45 +-- .../api/library/remove_library.py | 49 ++- .../api/library/remove_reference.py | 47 ++- .../api/library/unassign_reference.py | 102 +++-- .../ifcopenshell/api/material/__init__.py | 25 ++ .../api/material/add_constituent.py | 119 +++--- .../ifcopenshell/api/material/add_layer.py | 117 +++--- .../api/material/add_list_item.py | 107 +++-- .../ifcopenshell/api/material/add_material.py | 97 +++-- .../api/material/add_material_set.py | 159 ++++---- .../ifcopenshell/api/material/add_profile.py | 136 ++++--- .../api/material/assign_material.py | 254 ++++++------ .../api/material/assign_profile.py | 145 +++---- .../api/material/copy_material.py | 67 ++-- .../api/material/edit_assigned_material.py | 41 +- .../api/material/edit_constituent.py | 75 ++-- .../ifcopenshell/api/material/edit_layer.py | 77 ++-- .../api/material/edit_layer_usage.py | 97 +++-- .../api/material/edit_material.py | 15 +- .../ifcopenshell/api/material/edit_profile.py | 115 +++--- .../api/material/edit_profile_usage.py | 147 +++---- .../api/material/remove_constituent.py | 57 ++- .../ifcopenshell/api/material/remove_layer.py | 63 ++- .../api/material/remove_list_item.py | 61 ++- .../api/material/remove_material.py | 91 +++-- .../api/material/remove_material_set.py | 101 +++-- .../api/material/remove_profile.py | 83 ++-- .../api/material/reorder_set_item.py | 85 ++-- .../api/material/unassign_material.py | 71 ++-- .../ifcopenshell/api/nest/__init__.py | 5 + .../ifcopenshell/api/nest/assign_object.py | 260 ++++++------ .../ifcopenshell/api/nest/change_nest.py | 47 ++- .../ifcopenshell/api/nest/reorder_nesting.py | 29 +- .../ifcopenshell/api/nest/unassign_object.py | 89 ++--- .../ifcopenshell/api/owner/__init__.py | 24 ++ .../ifcopenshell/api/owner/add_actor.py | 71 ++-- .../ifcopenshell/api/owner/add_address.py | 87 ++-- .../ifcopenshell/api/owner/add_application.py | 88 +++-- .../api/owner/add_organisation.py | 57 ++- .../ifcopenshell/api/owner/add_person.py | 72 ++-- .../api/owner/add_person_and_organisation.py | 56 ++- .../ifcopenshell/api/owner/add_role.py | 71 ++-- .../ifcopenshell/api/owner/assign_actor.py | 139 ++++--- .../api/owner/create_owner_history.py | 165 ++++---- .../ifcopenshell/api/owner/edit_actor.py | 55 ++- .../ifcopenshell/api/owner/edit_address.py | 61 ++- .../api/owner/edit_organisation.py | 43 +- .../ifcopenshell/api/owner/edit_person.py | 43 +- .../ifcopenshell/api/owner/edit_role.py | 47 ++- .../ifcopenshell/api/owner/remove_actor.py | 49 ++- .../ifcopenshell/api/owner/remove_address.py | 47 ++- .../api/owner/remove_application.py | 33 +- .../api/owner/remove_organisation.py | 83 ++-- .../ifcopenshell/api/owner/remove_person.py | 81 ++-- .../owner/remove_person_and_organisation.py | 65 ++- .../ifcopenshell/api/owner/remove_role.py | 53 ++- .../ifcopenshell/api/owner/unassign_actor.py | 89 ++--- .../api/owner/update_owner_history.py | 115 +++--- .../ifcopenshell/api/profile/__init__.py | 6 + .../api/profile/add_arbitrary_profile.py | 67 ++-- .../add_arbitrary_profile_with_voids.py | 85 ++-- .../api/profile/add_parameterized_profile.py | 43 +- .../ifcopenshell/api/profile/edit_profile.py | 45 +-- .../api/profile/remove_profile.py | 45 +-- .../ifcopenshell/api/project/__init__.py | 5 + .../ifcopenshell/api/project/append_asset.py | 189 ++++----- .../api/project/assign_declaration.py | 210 +++++----- .../ifcopenshell/api/project/create_file.py | 74 ++-- .../api/project/unassign_declaration.py | 90 ++--- .../ifcopenshell/api/pset/__init__.py | 6 + .../ifcopenshell/api/pset/add_pset.py | 222 +++++------ .../ifcopenshell/api/pset/add_qto.py | 119 +++--- .../ifcopenshell/api/pset/edit_pset.py | 271 ++++++------- .../ifcopenshell/api/pset/edit_qto.py | 183 ++++----- .../ifcopenshell/api/pset/remove_pset.py | 113 +++--- .../api/pset_template/__init__.py | 7 + .../api/pset_template/add_prop_template.py | 160 ++++---- .../api/pset_template/add_pset_template.py | 152 ++++--- .../api/pset_template/edit_prop_template.py | 47 ++- .../api/pset_template/edit_pset_template.py | 45 +-- .../api/pset_template/remove_prop_template.py | 57 ++- .../api/pset_template/remove_pset_template.py | 37 +- .../ifcopenshell/api/resource/__init__.py | 13 + .../ifcopenshell/api/resource/add_resource.py | 172 ++++---- .../api/resource/add_resource_quantity.py | 87 ++-- .../api/resource/add_resource_time.py | 71 ++-- .../api/resource/assign_resource.py | 156 ++++---- .../api/resource/calculate_resource_usage.py | 53 +-- .../api/resource/calculate_resource_work.py | 79 ++-- .../api/resource/edit_resource.py | 43 +- .../api/resource/edit_resource_quantity.py | 63 ++- .../api/resource/edit_resource_time.py | 115 +++--- .../api/resource/remove_resource.py | 123 +++--- .../api/resource/remove_resource_quantity.py | 45 +-- .../api/resource/unassign_resource.py | 98 +++-- .../ifcopenshell/api/root/__init__.py | 5 + .../ifcopenshell/api/root/copy_class.py | 93 ++--- .../ifcopenshell/api/root/create_entity.py | 114 +++--- .../ifcopenshell/api/root/reassign_class.py | 110 +++--- .../ifcopenshell/api/root/remove_product.py | 371 +++++++++--------- .../ifcopenshell/api/sequence/__init__.py | 44 +++ .../ifcopenshell/api/sequence/add_task.py | 318 ++++++++------- .../api/sequence/add_task_time.py | 83 ++-- .../api/sequence/add_time_period.py | 119 +++--- .../api/sequence/add_work_calendar.py | 125 +++--- .../api/sequence/add_work_plan.py | 111 +++--- .../api/sequence/add_work_schedule.py | 188 +++++---- .../api/sequence/add_work_time.py | 105 +++-- .../api/sequence/assign_lag_time.py | 136 +++---- .../api/sequence/assign_process.py | 168 ++++---- .../api/sequence/assign_product.py | 128 +++--- .../api/sequence/assign_recurrence_pattern.py | 167 ++++---- .../api/sequence/assign_sequence.py | 194 +++++---- .../api/sequence/assign_workplan.py | 79 ++-- .../api/sequence/calculate_task_duration.py | 149 ++++--- .../api/sequence/cascade_schedule.py | 245 ++++++------ .../api/sequence/create_baseline.py | 73 ++-- .../api/sequence/duplicate_task.py | 55 +-- .../api/sequence/edit_lag_time.py | 117 +++--- .../api/sequence/edit_recurrence_pattern.py | 67 ++-- .../api/sequence/edit_sequence.py | 81 ++-- .../ifcopenshell/api/sequence/edit_task.py | 53 ++- .../api/sequence/edit_task_time.py | 147 +++---- .../api/sequence/edit_work_calendar.py | 45 +-- .../api/sequence/edit_work_plan.py | 55 ++- .../api/sequence/edit_work_schedule.py | 61 ++- .../api/sequence/edit_work_time.py | 82 ++-- .../api/sequence/get_related_products.py | 93 +++-- .../api/sequence/recalculate_schedule.py | 184 ++++----- .../ifcopenshell/api/sequence/remove_task.py | 209 +++++----- .../api/sequence/remove_time_period.py | 61 ++- .../api/sequence/remove_work_calendar.py | 75 ++-- .../api/sequence/remove_work_plan.py | 57 ++- .../api/sequence/remove_work_schedule.py | 111 +++--- .../api/sequence/remove_work_time.py | 39 +- .../api/sequence/unassign_lag_time.py | 83 ++-- .../api/sequence/unassign_process.py | 89 ++--- .../api/sequence/unassign_product.py | 89 ++--- .../sequence/unassign_recurrence_pattern.py | 53 ++- .../api/sequence/unassign_sequence.py | 79 ++-- .../ifcopenshell/api/spatial/__init__.py | 5 + .../api/spatial/assign_container.py | 262 ++++++------- .../api/spatial/dereference_structure.py | 108 +++-- .../api/spatial/reference_structure.py | 162 ++++---- .../api/spatial/unassign_container.py | 81 ++-- .../ifcopenshell/api/structural/__init__.py | 22 ++ .../api/structural/add_structural_activity.py | 116 +++--- .../add_structural_analysis_model.py | 37 +- .../add_structural_boundary_condition.py | 85 ++-- .../api/structural/add_structural_load.py | 49 ++- .../structural/add_structural_load_case.py | 63 ++- .../structural/add_structural_load_group.py | 63 ++- .../add_structural_member_connection.py | 47 ++- .../assign_structural_analysis_model.py | 61 ++- .../edit_structural_analysis_model.py | 33 +- .../edit_structural_boundary_condition.py | 43 +- .../edit_structural_connection_cs.py | 61 ++- .../structural/edit_structural_item_axis.py | 31 +- .../api/structural/edit_structural_load.py | 31 +- .../structural/edit_structural_load_case.py | 31 +- .../remove_structural_analysis_model.py | 39 +- .../remove_structural_boundary_condition.py | 49 ++- .../remove_structural_connection_condition.py | 41 +- .../api/structural/remove_structural_load.py | 21 +- .../structural/remove_structural_load_case.py | 37 +- .../remove_structural_load_group.py | 41 +- .../unassign_structural_analysis_model.py | 57 ++- .../ifcopenshell/api/style/__init__.py | 13 + .../ifcopenshell/api/style/add_style.py | 71 ++-- .../api/style/add_surface_style.py | 193 +++++---- .../api/style/add_surface_textures.py | 67 ++-- .../api/style/assign_material_style.py | 173 ++++---- .../api/style/assign_representation_styles.py | 184 ++++----- .../api/style/edit_presentation_style.py | 43 +- .../api/style/edit_surface_style.py | 111 +++--- .../ifcopenshell/api/style/remove_style.py | 49 +-- .../api/style/remove_styled_representation.py | 51 ++- .../api/style/remove_surface_style.py | 71 ++-- .../api/style/unassign_material_style.py | 131 +++---- .../style/unassign_representation_styles.py | 77 ++-- .../ifcopenshell/api/system/__init__.py | 13 + .../ifcopenshell/api/system/add_port.py | 63 ++- .../ifcopenshell/api/system/add_system.py | 67 ++-- .../api/system/assign_flow_control.py | 103 +++-- .../ifcopenshell/api/system/assign_port.py | 81 ++-- .../ifcopenshell/api/system/assign_system.py | 72 ++-- .../ifcopenshell/api/system/connect_port.py | 157 ++++---- .../api/system/disconnect_port.py | 93 +++-- .../ifcopenshell/api/system/edit_system.py | 43 +- .../ifcopenshell/api/system/remove_system.py | 87 ++-- .../api/system/unassign_flow_control.py | 91 +++-- .../ifcopenshell/api/system/unassign_port.py | 73 ++-- .../api/system/unassign_system.py | 64 ++- .../ifcopenshell/api/type/__init__.py | 5 + .../ifcopenshell/api/type/assign_type.py | 320 +++++++-------- .../api/type/get_related_objects.py | 71 ++-- .../api/type/map_type_representations.py | 160 ++++---- .../ifcopenshell/api/type/unassign_type.py | 85 ++-- .../ifcopenshell/api/unit/__init__.py | 11 + .../api/unit/add_context_dependent_unit.py | 75 ++-- .../api/unit/add_conversion_based_unit.py | 107 +++-- .../api/unit/add_monetary_unit.py | 41 +- .../ifcopenshell/api/unit/add_si_unit.py | 71 ++-- .../ifcopenshell/api/unit/assign_unit.py | 110 +++--- .../api/unit/edit_derived_unit.py | 31 +- .../api/unit/edit_monetary_unit.py | 45 +-- .../ifcopenshell/api/unit/edit_named_unit.py | 63 ++- .../ifcopenshell/api/unit/remove_unit.py | 53 ++- .../ifcopenshell/api/unit/unassign_unit.py | 61 ++- .../ifcopenshell/api/void/__init__.py | 5 + .../ifcopenshell/api/void/add_filling.py | 159 ++++---- .../ifcopenshell/api/void/add_opening.py | 189 +++++---- .../ifcopenshell/api/void/remove_filling.py | 65 ++- .../ifcopenshell/api/void/remove_opening.py | 61 ++- 330 files changed, 13283 insertions(+), 13751 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py index 18731bf443..bf452c8e2b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py @@ -22,3 +22,6 @@ One common use is spatial elements, such as how a site has multiple buildings, and a building has multiple storeys. Another is for regular elements, such as how a wall is made out of members and coverings. """ + +from .assign_object import assign_object +from .unassign_object import unassign_object diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py index 3e9f866435..c1ffdc5a16 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py @@ -23,148 +23,144 @@ import ifcopenshell.util.placement from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_object: ifcopenshell.entity_instance, - ): - """Assigns object as an aggregate to the products +def assign_object( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_object: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns object as an aggregate to the products - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. - Typically used when you want to describe how large spaces are made up of - smaller spaces. For example large spatial elements (e.g. sites, - buidings) can be made out of smaller spatial elements (e.g. storeys, - spaces). + Typically used when you want to describe how large spaces are made up of + smaller spaces. For example large spatial elements (e.g. sites, + buidings) can be made out of smaller spatial elements (e.g. storeys, + spaces). - The largest space (typically the IfcSite) can then be aggregated in a - project. It is requirement for all spatial structures to be directly or - indirectly aggregated back to the IfcProject to create a hierarchy of - spaces. + The largest space (typically the IfcSite) can then be aggregated in a + project. It is requirement for all spatial structures to be directly or + indirectly aggregated back to the IfcProject to create a hierarchy of + spaces. - The other common usecase is when larger physical products are made up of - smaller physical products. For example, a stair might be made out of a - flight, a landing, a railing and so on. Or a wall might be made out of - stud members, and coverings. + The other common usecase is when larger physical products are made up of + smaller physical products. For example, a stair might be made out of a + flight, a landing, a railing and so on. Or a wall might be made out of + stud members, and coverings. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an aggregate relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an aggregate relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - IFC placements follow a convention where the placement is relative to - its parent in the spatial hierarchy. If your product has a placement, - its placement will be recalculated to follow this convention. + IFC placements follow a convention where the placement is relative to + its parent in the spatial hierarchy. If your product has a placement, + its placement will be recalculated to follow this convention. - :param products: The list of parts of the aggregate, typically of IfcElement or - IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance] - :param relating_object: The whole of the aggregate, typically an - IfcElement or IfcSpatialStructureElement subclass - :type relating_object: ifcopenshell.entity_instance - :return: The IfcRelAggregate relationship instance - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of parts of the aggregate, typically of IfcElement or + IfcSpatialStructureElement subclass + :type product: list[ifcopenshell.entity_instance] + :param relating_object: The whole of the aggregate, typically an + IfcElement or IfcSpatialStructureElement subclass + :type relating_object: ifcopenshell.entity_instance + :return: The IfcRelAggregate relationship instance + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project) - # The site has a building - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) - """ - self.file = file - self.settings = { - "products": products, - "relating_object": relating_object, - } + # The site has a building + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) + """ + settings = { + "products": products, + "relating_object": relating_object, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - products = set(self.settings["products"]) - relating_object = self.settings["relating_object"] - is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) + products = set(settings["products"]) + relating_object = settings["relating_object"] + is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) - previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() - products_without_aggregates: list[ifcopenshell.entity_instance] = [] - products_with_aggregates: list[ifcopenshell.entity_instance] = [] + previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() + products_without_aggregates: list[ifcopenshell.entity_instance] = [] + products_with_aggregates: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for product in products: - product_rel = next(iter(product.Decomposes), None) + # check if there is anything to change + for product in products: + product_rel = next(iter(product.Decomposes), None) - if product_rel is None: - products_without_aggregates.append(product) - continue + if product_rel is None: + products_without_aggregates.append(product) + continue - # either is_decomposed_by is None or product is part of different rel - if product_rel != is_decomposed_by: - previous_aggregates_rels.add(product_rel) - products_with_aggregates.append(product) + # either is_decomposed_by is None or product is part of different rel + if product_rel != is_decomposed_by: + previous_aggregates_rels.add(product_rel) + products_with_aggregates.append(product) - # products with already assigned aggregates will be skipped + # products with already assigned aggregates will be skipped - products_to_change = products_without_aggregates + products_with_aggregates - # nothing to change - if not products_to_change: - return is_decomposed_by + products_to_change = products_without_aggregates + products_with_aggregates + # nothing to change + if not products_to_change: + return is_decomposed_by - # can be either only aggregated or only contained at the same time - # some product might not be able to have a container - possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] - ifcopenshell.api.run("spatial.unassign_container", self.file, products=possibly_contained_products) + # can be either only aggregated or only contained at the same time + # some product might not be able to have a container + possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] + ifcopenshell.api.run("spatial.unassign_container", file, products=possibly_contained_products) - # unassign elements from previous aggregates - for decomposes in previous_aggregates_rels: - related_objects = set(decomposes.RelatedObjects) - products - if related_objects: - decomposes.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": decomposes}) - else: - history = decomposes.OwnerHistory - self.file.remove(decomposes) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new aggregate - if is_decomposed_by: - is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by}) + # unassign elements from previous aggregates + for decomposes in previous_aggregates_rels: + related_objects = set(decomposes.RelatedObjects) - products + if related_objects: + decomposes.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": decomposes}) else: - is_decomposed_by = self.file.create_entity( - "IfcRelAggregates", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": list(products), - "RelatingObject": relating_object, - } + history = decomposes.OwnerHistory + file.remove(decomposes) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new aggregate + if is_decomposed_by: + is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_decomposed_by}) + else: + is_decomposed_by = file.create_entity( + "IfcRelAggregates", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": list(products), + "RelatingObject": relating_object, + } + ) + + # localize placement relative to a new aggregate for affected products + for product in products_to_change: + placement = getattr(product, "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=product, + matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), + is_si=False, ) - # localize placement relative to a new aggregate for affected products - for product in products_to_change: - placement = getattr(product, "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=product, - matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), - is_si=False, - ) - - return is_decomposed_by + return is_decomposed_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py index c766a4f019..8a8e35a948 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py @@ -21,60 +21,57 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Unassigns products from their aggregate +def unassign_object(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Unassigns products from their aggregate - A product (i.e. a smaller part of a whole) may be aggregated into zero - or one larger space or element. This function will remove that - aggregation relationship. + A product (i.e. a smaller part of a whole) may be aggregated into zero + or one larger space or element. This function will remove that + aggregation relationship. - As all physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", using this function will remove the - product from that tree. This is a dangerous operation and may result in - the product no longer being visible in IFC applications. + As all physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", using this function will remove the + product from that tree. This is a dangerous operation and may result in + the product no longer being visible in IFC applications. - If the product is not part of an aggregation relationship, nothing will - happen. + If the product is not part of an aggregation relationship, nothing will + happen. - :param products: The list of parts of the aggregate, typically of IfcElements or - IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param products: The list of parts of the aggregate, typically of IfcElements or + IfcSpatialStructureElement subclass + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - subelement1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - subelement2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement1], relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element) - # nothing is returned - ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1]) - # nothing is returned, relationship is removed - ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2]) - """ - self.file = file - self.settings = {"products": products} + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + subelement1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + subelement2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement1], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element) + # nothing is returned + ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1]) + # nothing is returned, relationship is removed + ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2]) + """ + settings = {"products": products} - def execute(self) -> None: - products = set(self.settings["products"]) - rels = set( - rel - for product in products - if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None)) - ) + products = set(settings["products"]) + rels = set( + rel + for product in products + if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None)) + ) - for rel in rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py index e0caddbe3c..31a605de5d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py @@ -15,3 +15,5 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .edit_attributes import edit_attributes diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py index 1e2cd98bc5..05dbff5a5c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py @@ -19,64 +19,49 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, product=None, attributes=None): - """Edit the attributes of a product +def edit_attributes(file, product=None, attributes=None) -> None: + """Edit the attributes of a product - All IFC entities have attributes. Normally they can be edited directly, - by simply assigning a new value to them. In some scenarios, you may wish - to also ensure that ownership history is updated. This function provides - that convenience. + All IFC entities have attributes. Normally they can be edited directly, + by simply assigning a new value to them. In some scenarios, you may wish + to also ensure that ownership history is updated. This function provides + that convenience. - :param product: The product you want to edit. This may be any rooted IFC - entity. - :type product: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param product: The product you want to edit. This may be any rooted IFC + entity. + :type product: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("attribute.edit_attributes", model, - product=element, attributes={"Name": "Waldo"}) - """ - self.file = file - self.settings = {"product": product, "attributes": attributes or {}} + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("attribute.edit_attributes", model, + product=element, attributes={"Name": "Waldo"}) + """ + settings = {"product": product, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["product"], name, value) - if hasattr(self.settings["product"], "PredefinedType"): - if hasattr(self.settings["product"], "ElementType"): - if ( - self.settings["product"].ElementType is None - and self.settings["product"].PredefinedType == "USERDEFINED" - ): - self.settings["product"].PredefinedType = "NOTDEFINED" - elif ( - self.settings["product"].ElementType - and self.settings["product"].PredefinedType != "USERDEFINED" - ): - self.settings["product"].PredefinedType = "USERDEFINED" - elif hasattr(self.settings["product"], "ObjectType"): - relating_type = ifcopenshell.util.element.get_type(self.settings["product"]) - # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 - if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None): - self.settings["product"].ObjectType = None - self.settings["product"].PredefinedType = None - elif ( - self.settings["product"].ObjectType is None - and self.settings["product"].PredefinedType == "USERDEFINED" - ): - self.settings["product"].PredefinedType = "NOTDEFINED" - elif ( - self.settings["product"].ObjectType - and self.settings["product"].PredefinedType != "USERDEFINED" - ): - self.settings["product"].PredefinedType = "USERDEFINED" - if hasattr(self.settings["product"], "OwnerHistory"): - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": self.settings["product"]}) + for name, value in settings["attributes"].items(): + setattr(settings["product"], name, value) + if hasattr(settings["product"], "PredefinedType"): + if hasattr(settings["product"], "ElementType"): + if settings["product"].ElementType is None and settings["product"].PredefinedType == "USERDEFINED": + settings["product"].PredefinedType = "NOTDEFINED" + elif settings["product"].ElementType and settings["product"].PredefinedType != "USERDEFINED": + settings["product"].PredefinedType = "USERDEFINED" + elif hasattr(settings["product"], "ObjectType"): + relating_type = ifcopenshell.util.element.get_type(settings["product"]) + # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 + if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None): + settings["product"].ObjectType = None + settings["product"].PredefinedType = None + elif settings["product"].ObjectType is None and settings["product"].PredefinedType == "USERDEFINED": + settings["product"].PredefinedType = "NOTDEFINED" + elif settings["product"].ObjectType and settings["product"].PredefinedType != "USERDEFINED": + settings["product"].PredefinedType = "USERDEFINED" + if hasattr(settings["product"], "OwnerHistory"): + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": settings["product"]}) diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py index c2a0c1900d..fff4c4e7f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py @@ -19,3 +19,8 @@ """Boundaries are primarily used for representing virtual interfaces between spaces for energy analysis. """ + +from .assign_connection_geometry import assign_connection_geometry +from .copy_boundary import copy_boundary +from .edit_attributes import edit_attributes +from .remove_boundary import remove_boundary diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py index 8f23a60bb6..b184810642 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py @@ -19,68 +19,80 @@ import ifcopenshell.util.unit +def assign_connection_geometry( + file, + rel_space_boundary=None, + outer_boundary=None, + inner_boundaries=None, + location=None, + axis=None, + ref_direction=None, + unit_scale=None, +) -> None: + """Create and assign a connection geometry to a space boundary relationship + + A space boundary may optionally have a plane that represents how that + space is adjacent to another space, known as the connection geometry. + You may specify this plane in terms of an outer boundary polyline, zero + or more inner boundaries (such as for windows), and a positional matrix + for the orientation of the plane. + + :param rel_space_boundary: The space boundary relationship to assign the + connection geometry to. + :type rel_space_boundary: ifcopenshell.entity_instance + :param outer_boundary: A list of 2D points representing an open + polyline. The last point will connect to the first point. Each + point is represented by an interable of 2 floats. The coordinates of + the points are relative to the positional matrix arguments. + :type outer_boundary: list[list[float]] + :param inner_boundaries: A list of zero or more inner boundaries to use + for the plane. Each boundary is represented by an open polyline, as + defined by the outer_boundary argument. + :type inner_boundaries: list[list[list[float]]], optional + :param location: The local origin of the connection geometry, defined as + an XYZ coordinate relative to the placement of the space that is + being bounded. + :type location: list[float] + :param axis: The local X axis of the connection geometry, defined as an + XYZ vector relative to the placement of the space that is being + bounded. + :type axis: list[float] + :param ref_direction: The local Z axis of the connection geometry, + defined as an XYZ vector relative to the placement of the space that + is being bounded. The Y vector is automatically derived using the + right hand rule. + :type ref_direction: list[float] + :param unit_scale: The unit scale as calculated by + ifcopenshell.util.unit.calculate_unit_scale. If not provided, it + will be automatically calculated for you. + :type unit_scale: float, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("boundary.assign_connection_geometry", model, + rel_space_boundary=element, + outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)], + location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.], + ) + """ + usecase = Usecase() + usecase.file = file + usecase.rel_space_boundary = rel_space_boundary + usecase.outer_boundary = outer_boundary + usecase.inner_boundaries = inner_boundaries or () + usecase.location = location + usecase.axis = axis + usecase.ref_direction = ref_direction + usecase.unit_scale = unit_scale + usecase.ifc_vertices = [] + return usecase.execute() + + class Usecase: - def __init__(self, file, rel_space_boundary=None, outer_boundary=None, inner_boundaries=None, location=None, axis=None, ref_direction=None, unit_scale=None): - """Create and assign a connection geometry to a space boundary relationship - - A space boundary may optionally have a plane that represents how that - space is adjacent to another space, known as the connection geometry. - You may specify this plane in terms of an outer boundary polyline, zero - or more inner boundaries (such as for windows), and a positional matrix - for the orientation of the plane. - - :param rel_space_boundary: The space boundary relationship to assign the - connection geometry to. - :type rel_space_boundary: ifcopenshell.entity_instance - :param outer_boundary: A list of 2D points representing an open - polyline. The last point will connect to the first point. Each - point is represented by an interable of 2 floats. The coordinates of - the points are relative to the positional matrix arguments. - :type outer_boundary: list[list[float]] - :param inner_boundaries: A list of zero or more inner boundaries to use - for the plane. Each boundary is represented by an open polyline, as - defined by the outer_boundary argument. - :type inner_boundaries: list[list[list[float]]], optional - :param location: The local origin of the connection geometry, defined as - an XYZ coordinate relative to the placement of the space that is - being bounded. - :type location: list[float] - :param axis: The local X axis of the connection geometry, defined as an - XYZ vector relative to the placement of the space that is being - bounded. - :type axis: list[float] - :param ref_direction: The local Z axis of the connection geometry, - defined as an XYZ vector relative to the placement of the space that - is being bounded. The Y vector is automatically derived using the - right hand rule. - :type ref_direction: list[float] - :param unit_scale: The unit scale as calculated by - ifcopenshell.util.unit.calculate_unit_scale. If not provided, it - will be automatically calculated for you. - :type unit_scale: float, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("boundary.assign_connection_geometry", model, - rel_space_boundary=element, - outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)], - location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.], - ) - """ - self.file = file - self.rel_space_boundary = rel_space_boundary - self.outer_boundary = outer_boundary - self.inner_boundaries = inner_boundaries or () - self.location = location - self.axis = axis - self.ref_direction = ref_direction - self.unit_scale = unit_scale - self.ifc_vertices = [] - def execute(self): if self.unit_scale is None: self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py index 2f8b092c51..b051bae828 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py @@ -19,29 +19,26 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, boundary=None): - """Copies a space boundary +def copy_boundary(file, boundary=None) -> None: + """Copies a space boundary - :param boundary: The IfcRelSpaceBoundary you want to copy. - :type boundary: ifcopenshell.entity_instance - :return: None - :rtype: None + :param boundary: The IfcRelSpaceBoundary you want to copy. + :type boundary: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A boring boundary with no geometry. Note that this boundary is - # invalid and does not relate to any space or building element. - boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") + # A boring boundary with no geometry. Note that this boundary is + # invalid and does not relate to any space or building element. + boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") - # And now we have two - boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary) - """ - self.file = file - self.settings = {"boundary": boundary} + # And now we have two + boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary) + """ + settings = {"boundary": boundary} - def execute(self): - result = ifcopenshell.util.element.copy(self.file, self.settings["boundary"]) - if result.ConnectionGeometry: - result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(self.file, result.ConnectionGeometry) - return result + result = ifcopenshell.util.element.copy(file, settings["boundary"]) + if result.ConnectionGeometry: + result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry) + return result diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py index 4be540f7a2..663c656dbb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py @@ -17,45 +17,49 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, entity=None, relating_space=None, related_building_element=None, parent_boundary=None, corresponding_boundary=None): - """Modify the relationships of a space boundary relationship +def edit_attributes( + file, + entity=None, + relating_space=None, + related_building_element=None, + parent_boundary=None, + corresponding_boundary=None, +) -> None: + """Modify the relationships of a space boundary relationship - Currently this function is quite minimal and offers no advantage to - manual assignment of the space boundary attributes. + Currently this function is quite minimal and offers no advantage to + manual assignment of the space boundary attributes. - :param entity: The IfcRelSpaceBoundary to modify - :type entity: ifcopenshell.entity_instance - :param relating_space: The IfcSpace or IfcExternalSpatialElement that - the space boundary is related to. - :type relating_space: ifcopenshell.entity_instance - :param related_building_element: The IfcElement that defines the - boundary, typically an IfcWall. - :type relating_space: ifcopenshell.entity_instance - :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if - this is an inner boundary. This can apply to 1st and 2nd level - boundaries. - :type parent_boundary: ifcopenshell.entity_instance, - optional - :param corresponding_boundary: The other IfcRelSpaceBoundary on the - other side of the related element. The pair together represents a - thermal boundary. This only applies to 2nd level boundaries. - :type corresponding_boundary: ifcopenshell.entity_instance, - optional - :return: None - :rtype: None - """ - self.file = file - self.entity = entity - self.relating_space = relating_space - self.related_building_element = related_building_element - self.parent_boundary = parent_boundary - self.corresponding_boundary = corresponding_boundary + :param entity: The IfcRelSpaceBoundary to modify + :type entity: ifcopenshell.entity_instance + :param relating_space: The IfcSpace or IfcExternalSpatialElement that + the space boundary is related to. + :type relating_space: ifcopenshell.entity_instance + :param related_building_element: The IfcElement that defines the + boundary, typically an IfcWall. + :type relating_space: ifcopenshell.entity_instance + :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if + this is an inner boundary. This can apply to 1st and 2nd level + boundaries. + :type parent_boundary: ifcopenshell.entity_instance, + optional + :param corresponding_boundary: The other IfcRelSpaceBoundary on the + other side of the related element. The pair together represents a + thermal boundary. This only applies to 2nd level boundaries. + :type corresponding_boundary: ifcopenshell.entity_instance, + optional + :return: None + :rtype: None + """ + entity = entity + relating_space = relating_space + related_building_element = related_building_element + parent_boundary = parent_boundary + corresponding_boundary = corresponding_boundary - def execute(self): - self.entity.RelatingSpace = self.relating_space - self.entity.RelatedBuildingElement = self.related_building_element - if hasattr(self.entity, "ParentBoundary"): - self.entity.ParentBoundary = self.parent_boundary - if hasattr(self.entity, "CorrespondingBoundary"): - self.entity.CorrespondingBoundary = self.corresponding_boundary + entity.RelatingSpace = relating_space + entity.RelatedBuildingElement = related_building_element + if hasattr(entity, "ParentBoundary"): + entity.ParentBoundary = parent_boundary + if hasattr(entity, "CorrespondingBoundary"): + entity.CorrespondingBoundary = corresponding_boundary diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py index 6744da820c..dadf44e3c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, boundary=None): - """Removes a space boundary +def remove_boundary(file, boundary=None) -> None: + """Removes a space boundary - The relating space or related building element is untouched. Only the - boundary and its connection geometry is removed. + The relating space or related building element is untouched. Only the + boundary and its connection geometry is removed. - :param boundary: The IfcRelSpaceBoundary you want to remove. - :type boundary: ifcopenshell.entity_instance - :return: None - :rtype: None + :param boundary: The IfcRelSpaceBoundary you want to remove. + :type boundary: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A boring boundary with no geometry. Note that this boundary is - # invalid and does not relate to any space or building element. - boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") + # A boring boundary with no geometry. Note that this boundary is + # invalid and does not relate to any space or building element. + boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") - # Let's remove it! - ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary) - """ - self.file = file - self.settings = {"boundary": boundary} + # Let's remove it! + ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary) + """ + settings = {"boundary": boundary} - def execute(self): - geometry = self.settings["boundary"].ConnectionGeometry - if geometry: - self.settings["boundary"].ConnectionGeometry = None - ifcopenshell.util.element.remove_deep2(self.file, geometry) - history = self.settings["boundary"].OwnerHistory - self.file.remove(self.settings["boundary"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + geometry = settings["boundary"].ConnectionGeometry + if geometry: + settings["boundary"].ConnectionGeometry = None + ifcopenshell.util.element.remove_deep2(file, geometry) + history = settings["boundary"].OwnerHistory + file.remove(settings["boundary"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py index e0caddbe3c..6616ff6f89 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_classification import add_classification +from .add_reference import add_reference +from .edit_classification import edit_classification +from .edit_reference import edit_reference +from .remove_classification import remove_classification +from .remove_reference import remove_reference diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py index 580c036a5f..a6e251fcf2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py @@ -22,67 +22,72 @@ import ifcopenshell.util.date from typing import Union +def add_classification( + file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance] +) -> ifcopenshell.entity_instance: + """Adds a new classification system to the project + + External classification systems such as Uniclass or Omniclass are + ways of categorising elements in the AEC industry, typically + standardised or nominated by governments or companies. A system + typically contains a series of hierarchical reference codes and labels + like Pr_12_23_34. + + Classifications may be applied to many things, not just physical + elements, such as doors and windows, spatial elements, tasks, cost + items, or even resources. + + Prior to assigning classificaion references, you need to add the name + and metadata of the classification system that you will use in your + project. Classification systems may be revised over time, so this + metadata includes the edition date. + + Common classification systems are provided as an IFC library which may + be downloaded from https://github.com/Moult/IfcClassification for your + convenience. It is advised to use these to ensure that the + classification metadata is standardised. + + Adding a classification system will not add the entire hierarchy of + references available in the classification. References need to be added + separately. Typically, you'd only add the references that you use in + your project, see ifcopenshell.api.classification.add_reference for more + information. + + :param classification: If a string is provided, it is assumed to be the + name of your classification system. This is necessary if you are + creating your own custom classification system. Alternatively, you + may provide an entity_instance of an IfcClassification from an IFC + classification library. The latter approach is preferred if you are + using a commonly known system such as Uniclass, as this will ensure + all metadata is added correctly. + :type classification: str,ifcopenshell.entity_instance + :return: The added IfcClassification element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Option 1: adding a custom clasification from scratch + ifcopenshell.api.run("classification.add_classification", model, + classification="MyCustomClassification") + + # Option 2: adding a popular classification from a library + library = ifcopenshell.open("/path/to/Uniclass.ifc") + classification = library.by_type("IfcClassification")[0] + ifcopenshell.api.run("classification.add_classification", model, + classification=classification) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "classification": classification, + } + return usecase.execute() + + class Usecase: - def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]): - """Adds a new classification system to the project - - External classification systems such as Uniclass or Omniclass are - ways of categorising elements in the AEC industry, typically - standardised or nominated by governments or companies. A system - typically contains a series of hierarchical reference codes and labels - like Pr_12_23_34. - - Classifications may be applied to many things, not just physical - elements, such as doors and windows, spatial elements, tasks, cost - items, or even resources. - - Prior to assigning classificaion references, you need to add the name - and metadata of the classification system that you will use in your - project. Classification systems may be revised over time, so this - metadata includes the edition date. - - Common classification systems are provided as an IFC library which may - be downloaded from https://github.com/Moult/IfcClassification for your - convenience. It is advised to use these to ensure that the - classification metadata is standardised. - - Adding a classification system will not add the entire hierarchy of - references available in the classification. References need to be added - separately. Typically, you'd only add the references that you use in - your project, see ifcopenshell.api.classification.add_reference for more - information. - - :param classification: If a string is provided, it is assumed to be the - name of your classification system. This is necessary if you are - creating your own custom classification system. Alternatively, you - may provide an entity_instance of an IfcClassification from an IFC - classification library. The latter approach is preferred if you are - using a commonly known system such as Uniclass, as this will ensure - all metadata is added correctly. - :type classification: str,ifcopenshell.entity_instance - :return: The added IfcClassification element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Option 1: adding a custom clasification from scratch - ifcopenshell.api.run("classification.add_classification", model, - classification="MyCustomClassification") - - # Option 2: adding a popular classification from a library - library = ifcopenshell.open("/path/to/Uniclass.ifc") - classification = library.by_type("IfcClassification")[0] - ifcopenshell.api.run("classification.add_classification", model, - classification=classification) - """ - self.file = file - self.settings = { - "classification": classification, - } - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): if isinstance(self.settings["classification"], str): classification = self.file.createIfcClassification(Name=self.settings["classification"]) self.relate_to_project(classification) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index db1bab41bf..979bfc40dd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -23,117 +23,119 @@ import ifcopenshell.util.schema from typing import Optional, Union +def add_reference( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + reference: Optional[ifcopenshell.entity_instance] = None, + identification: Optional[str] = None, + name: Optional[str] = None, + classification: Optional[ifcopenshell.entity_instance] = None, + is_lightweight=True, +) -> Union[ifcopenshell.entity_instance, None]: + """Adds a new classification reference and assigns it to the list of products + + A classification reference is a single entry such as "Pr_12_23_34" that + is part of an external classification system (such as Uniclass or + Omniclass). + + References can be added to almost any object in IFC, including physical + objects, object types, properties, tasks, costs, resources, or even + resources such as profiles, documents, libraries, and so on. + + Classification references can be added in two ways. Option 1) specify a + custom arbitrary reference, where you have to manually specify the + identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products"). + Option 2) add a reference from an IFC classification library. The latter + is preferred if you are using a common classification system such as + Uniclass, as the library will be prepopulated with all the valid + classifications already. + + Objects are allowed to have multiple classification references from + multiple classification systems. This means that adding a new reference + will not remove existing references. + + References can be inherited from types. This means that if an + IfcWallType has a classification reference of Pr_12_23_34, then all + IfcWall occurrences of that type automatically get the same + classification of Pr_12_23_34. This means that it is more efficient to + assign to types where possible. If a classification reference is + assigned to both the type and an occurrence, then the assignment at the + occurrence will override the type classification. + + :param product: The list of IFC objects, properties, or resources you want to + associate the classification reference to. + :type product: list[ifcopenshell.entity_instance] + :param reference: The classification reference entity taken from an + IFC classification library. If you supply this parameter, you will + use option 2. + :type reference: ifcopenshell.entity_instance, optional + :param identification: If you choose option 1 and do not specify a + reference, you may manually specify an identification code. The code + is typically a short identifier and may have punctuation to separate + the levels of hierarchy in the classificaion (e.g. Pr_12_23_34). + :type identification: str, optional + :param name: If you choose option 1 and do not specify a reference, you + may manually specify a name. The name is typically human readable. + :type name: str, optional + :param classification: The IfcClassification entity in your IFC model + (not the library, if you are doing option 2) that the reference is + part of. + :type classification: ifcopenshell.entity_instance + :param is_lightweight: If you are doing option 2, choose whether or not + to only add that particular reference (lighweight) or also add all + of its parent references in the classification hierarchy (not + lighweight). For example, adding a lightweight reference to + Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference + to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent + references merely help describe the "tree" of classifications, but + is generally unnecessary. Using lightweight classifications are + recommended and is the default. + :type is_lightweight: bool, optional + + :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. + + :return: The newly added IfcClassificationReference + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] + + Example: + + .. code:: python + + # Option 1: adding and assigning a new reference from scratch + wall_type = model.by_type("IfcWallType")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification="MyCustomClassification") + ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + identification="W_01", name="Interior Walls") + + # Option 2: adding a popular classification from a library + library = ifcopenshell.open("/path/to/Uniclass.ifc") + lib_classification = library.by_type("IfcClassification")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification=lib_classification) + reference = [r for r in library.by_type("IfcClassificationReference") + if r.Identification == "XYZ"][0] + ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + reference=reference) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "reference": reference, + "identification": identification, + "name": name, + "classification": classification, + "is_lightweight": is_lightweight, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - reference: Optional[ifcopenshell.entity_instance] = None, - identification: Optional[str] = None, - name: Optional[str] = None, - classification: Optional[ifcopenshell.entity_instance] = None, - is_lightweight=True, - ): - """Adds a new classification reference and assigns it to the list of products - - A classification reference is a single entry such as "Pr_12_23_34" that - is part of an external classification system (such as Uniclass or - Omniclass). - - References can be added to almost any object in IFC, including physical - objects, object types, properties, tasks, costs, resources, or even - resources such as profiles, documents, libraries, and so on. - - Classification references can be added in two ways. Option 1) specify a - custom arbitrary reference, where you have to manually specify the - identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products"). - Option 2) add a reference from an IFC classification library. The latter - is preferred if you are using a common classification system such as - Uniclass, as the library will be prepopulated with all the valid - classifications already. - - Objects are allowed to have multiple classification references from - multiple classification systems. This means that adding a new reference - will not remove existing references. - - References can be inherited from types. This means that if an - IfcWallType has a classification reference of Pr_12_23_34, then all - IfcWall occurrences of that type automatically get the same - classification of Pr_12_23_34. This means that it is more efficient to - assign to types where possible. If a classification reference is - assigned to both the type and an occurrence, then the assignment at the - occurrence will override the type classification. - - :param product: The list of IFC objects, properties, or resources you want to - associate the classification reference to. - :type product: list[ifcopenshell.entity_instance] - :param reference: The classification reference entity taken from an - IFC classification library. If you supply this parameter, you will - use option 2. - :type reference: ifcopenshell.entity_instance, optional - :param identification: If you choose option 1 and do not specify a - reference, you may manually specify an identification code. The code - is typically a short identifier and may have punctuation to separate - the levels of hierarchy in the classificaion (e.g. Pr_12_23_34). - :type identification: str, optional - :param name: If you choose option 1 and do not specify a reference, you - may manually specify a name. The name is typically human readable. - :type name: str, optional - :param classification: The IfcClassification entity in your IFC model - (not the library, if you are doing option 2) that the reference is - part of. - :type classification: ifcopenshell.entity_instance - :param is_lightweight: If you are doing option 2, choose whether or not - to only add that particular reference (lighweight) or also add all - of its parent references in the classification hierarchy (not - lighweight). For example, adding a lightweight reference to - Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference - to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent - references merely help describe the "tree" of classifications, but - is generally unnecessary. Using lightweight classifications are - recommended and is the default. - :type is_lightweight: bool, optional - - :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. - - :return: The newly added IfcClassificationReference - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] - - Example: - - .. code:: python - - # Option 1: adding and assigning a new reference from scratch - wall_type = model.by_type("IfcWallType")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification="MyCustomClassification") - ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - identification="W_01", name="Interior Walls") - - # Option 2: adding a popular classification from a library - library = ifcopenshell.open("/path/to/Uniclass.ifc") - lib_classification = library.by_type("IfcClassification")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification=lib_classification) - reference = [r for r in library.by_type("IfcClassificationReference") - if r.Identification == "XYZ"][0] - ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - reference=reference) - """ - self.file = file - self.settings = { - "products": products, - "reference": reference, - "identification": identification, - "name": name, - "classification": classification, - "is_lightweight": is_lightweight, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): if not self.settings["products"]: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py index 9925c59f54..7568a11d5e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, classification=None, attributes=None): - """Edits the attributes of an IfcClassification +def edit_classification(file, classification=None, attributes=None) -> None: + """Edits the attributes of an IfcClassification - For more information about the attributes and data types of an - IfcClassification, consult the IFC documentation. + For more information about the attributes and data types of an + IfcClassification, consult the IFC documentation. - :param classification: The IfcClassification entity you want to edit - :type classification: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param classification: The IfcClassification entity you want to edit + :type classification: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - classification = model.by_type("IfcClassification")[0] - # Change the name of the classification system to "Foo" - ifcopenshell.api.run("classification.edit_classification", model, - classification=classification, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"classification": classification, "attributes": attributes or {}} + classification = model.by_type("IfcClassification")[0] + # Change the name of the classification system to "Foo" + ifcopenshell.api.run("classification.edit_classification", model, + classification=classification, attributes={"Name": "Foo"}) + """ + settings = {"classification": classification, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["classification"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["classification"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py index 4acf396adb..dc5096f38c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, reference=None, attributes=None): - """Edits the attributes of an IfcClassificationReference +def edit_reference(file, reference=None, attributes=None) -> None: + """Edits the attributes of an IfcClassificationReference - For more information about the attributes and data types of an - IfcClassificationReference, consult the IFC documentation. + For more information about the attributes and data types of an + IfcClassificationReference, consult the IFC documentation. - :param reference: The IfcClassificationReference entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcClassificationReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - reference = model.by_type("IfcClassification")[0] - # Change the name of the reference to "Foo" - ifcopenshell.api.run("classification.edit_reference", model, - reference=reference, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + reference = model.by_type("IfcClassification")[0] + # Change the name of the reference to "Foo" + ifcopenshell.api.run("classification.edit_reference", model, + reference=reference, attributes={"Name": "Foo"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py index 42a5dcacd0..42ec050d61 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -20,30 +20,33 @@ import ifcopenshell import ifcopenshell.util.element +def remove_classification(file, classification=None) -> None: + """Removes an IfcClassification from the project and all references + + The classification and all of its relationships, children references, + and relationships between objectse and child references are completely + removed from a project. + + :param classification: The IfcClassification entity you want to remove + :type classification: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + classification = model.by_type("IfcClassification")[0] + ifcopenshell.api.run("classification.remove_classification", model, + classification=classification) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"classification": classification} + return usecase.execute() + + class Usecase: - def __init__(self, file, classification=None): - """Removes an IfcClassification from the project and all references - - The classification and all of its relationships, children references, - and relationships between objectse and child references are completely - removed from a project. - - :param classification: The IfcClassification entity you want to remove - :type classification: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - classification = model.by_type("IfcClassification")[0] - ifcopenshell.api.run("classification.remove_classification", model, - classification=classification) - """ - self.file = file - self.settings = {"classification": classification} - def execute(self): references = self.get_references(self.settings["classification"]) for reference in references: diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py index ea61fb002d..bad8406582 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py @@ -21,107 +21,102 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - products: list[ifcopenshell.entity_instance], - ): - """Removes a classification reference from the list of products +def remove_reference( + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + products: list[ifcopenshell.entity_instance], +) -> None: + """Removes a classification reference from the list of products - If the classification reference is no longer associated to any products, - the classification reference itself is also removed. + If the classification reference is no longer associated to any products, + the classification reference itself is also removed. - :param reference: The IfcClassificationReference entity of the - relationship you want to remove. - :type reference: ifcopenshell.entity_instance - :param product: The list fo object entities of the relationship you want to - remove. - :type product: list[ifcopenshell.entity_instance] + :param reference: The IfcClassificationReference entity of the + relationship you want to remove. + :type reference: ifcopenshell.entity_instance + :param product: The list fo object entities of the relationship you want to + remove. + :type product: list[ifcopenshell.entity_instance] - :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. + :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - wall_type = model.by_type("IfcWallType")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification="MyCustomClassification") - reference = ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - identification="W_01", name="Interior Walls") - ifcopenshell.api.run("classification.remove_reference", model, - reference=reference, products=[wall_type]) - """ - self.file = file - self.settings = {"reference": reference, "products": products} + wall_type = model.by_type("IfcWallType")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification="MyCustomClassification") + reference = ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + identification="W_01", name="Interior Walls") + ifcopenshell.api.run("classification.remove_reference", model, + reference=reference, products=[wall_type]) + """ + settings = {"reference": reference, "products": products} - def execute(self) -> None: - is_ifc2x3 = self.file.schema == "IFC2X3" - products = set(self.settings["products"]) - referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - products -= products.difference(referenced) + is_ifc2x3 = file.schema == "IFC2X3" + products = set(settings["products"]) + referenced = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + products -= products.difference(referenced) - # all products are already unassigned from a reference - if not products: - return + # all products are already unassigned from a reference + if not products: + return - rooted_products: set[ifcopenshell.entity_instance] = set() - non_rooted_products: set[ifcopenshell.entity_instance] = set() - for product in self.settings["products"]: - if product.is_a("IfcRoot"): - rooted_products.add(product) + rooted_products: set[ifcopenshell.entity_instance] = set() + non_rooted_products: set[ifcopenshell.entity_instance] = set() + for product in settings["products"]: + if product.is_a("IfcRoot"): + rooted_products.add(product) + else: + non_rooted_products.add(product) + + if non_rooted_products and is_ifc2x3: + raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.") + + if rooted_products: + reference_rels: set[ifcopenshell.entity_instance] = set() + for product in rooted_products: + reference_rels.update(product.HasAssociations) + + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == settings["reference"] + } + + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - rooted_products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - non_rooted_products.add(product) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - if non_rooted_products and is_ifc2x3: - raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.") + if non_rooted_products: + reference_rels: set[ifcopenshell.entity_instance] = set() + for product in non_rooted_products: + rels = getattr(product, "HasExternalReferences", None) + if rels is None: + rels = getattr(product, "HasExternalReference", []) + reference_rels.update(rels) - if rooted_products: - reference_rels: set[ifcopenshell.entity_instance] = set() - for product in rooted_products: - reference_rels.update(product.HasAssociations) + reference_rels = {rel for rel in reference_rels if rel.RelatingReference == settings["reference"]} + for rel in reference_rels: + related_objects = set(rel.RelatedResourceObjects) - non_rooted_products + if related_objects: + rel.RelatedResourceObjects = list(related_objects) + else: + file.remove(rel) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesClassification") - and rel.RelatingClassification == self.settings["reference"] - } - - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - rooted_products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - if non_rooted_products: - reference_rels: set[ifcopenshell.entity_instance] = set() - for product in non_rooted_products: - rels = getattr(product, "HasExternalReferences", None) - if rels is None: - rels = getattr(product, "HasExternalReference", []) - reference_rels.update(rels) - - reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]} - for rel in reference_rels: - related_objects = set(rel.RelatedResourceObjects) - non_rooted_products - if related_objects: - rel.RelatedResourceObjects = list(related_objects) - else: - self.file.remove(rel) - - # TODO: we only handle lightweight classifications here - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - if not referenced_elements: - self.file.remove(self.settings["reference"]) + # TODO: we only handle lightweight classifications here + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + if not referenced_elements: + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py index e0caddbe3c..7309050851 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py @@ -15,3 +15,13 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_metric import add_metric +from .add_metric_reference import add_metric_reference +from .add_objective import add_objective +from .assign_constraint import assign_constraint +from .edit_metric import edit_metric +from .edit_objective import edit_objective +from .remove_constraint import remove_constraint +from .remove_metric import remove_metric +from .unassign_constraint import unassign_constraint diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py index b84e2f3cc9..ab0870b528 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py @@ -19,44 +19,41 @@ import ifcopenshell -class Usecase: - def __init__(self, file, objective=None): - """Add a new metric benchmark +def add_metric(file, objective=None) -> None: + """Add a new metric benchmark - Qualitative constraints may have a series of quantitative benchmarks - linked to it known as metrics. Metrics may be parametrically linked to - computed model properties or quantities. Metrics need to be satisfied - to meet the objective of the constraint. + Qualitative constraints may have a series of quantitative benchmarks + linked to it known as metrics. Metrics may be parametrically linked to + computed model properties or quantities. Metrics need to be satisfied + to meet the objective of the constraint. - :param objective: The IfcObjective that this metric is a benchmark of. - :type objective: ifcopenshell.entity_instance - :return: The newly created IfcMetric entity - :rtype: ifcopenshell.entity_instance + :param objective: The IfcObjective that this metric is a benchmark of. + :type objective: ifcopenshell.entity_instance + :return: The newly created IfcMetric entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - """ - self.file = file - self.settings = { - "objective": objective, + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + """ + settings = { + "objective": objective, + } + + metric = file.create_entity( + "IfcMetric", + **{ + "Name": "Unnamed", + "ConstraintGrade": "NOTDEFINED", + "Benchmark": "EQUALTO", } - - def execute(self): - metric = self.file.create_entity( - "IfcMetric", - **{ - "Name": "Unnamed", - "ConstraintGrade": "NOTDEFINED", - "Benchmark": "EQUALTO", - } - ) - if self.settings["objective"]: - benchmark_values = list(self.settings["objective"].BenchmarkValues or []) - benchmark_values.append(metric) - self.settings["objective"].BenchmarkValues = benchmark_values - return metric + ) + if settings["objective"]: + benchmark_values = list(settings["objective"].BenchmarkValues or []) + benchmark_values.append(metric) + settings["objective"].BenchmarkValues = benchmark_values + return metric diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py index 072c71ecb0..a3c37392e2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py @@ -18,28 +18,26 @@ import ifcopenshell -class Usecase: - def __init__(self, file, metric=None, reference_path=None): - """ - Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute" - Used to reference a value of an attribute of an instance through a metric objective entity. - """ - self.file = file - self.settings = {"metric": metric, "reference_path": reference_path} - def execute(self): - if self.settings["reference_path"]: - attributes = self.settings["reference_path"].split(".") - references_created = [] - for i in range(len(attributes)): - if i == 0: - reference = self.file.create_entity("IfcReference") - reference.AttributeIdentifier = attributes[i] - self.settings["metric"].ReferencePath = reference - references_created.append(reference) - else: - reference = self.file.create_entity("IfcReference") - reference.AttributeIdentifier = attributes[i] - references_created[i-1].InnerReference = reference - references_created.append(reference) - return references_created \ No newline at end of file +def add_metric_reference(file, metric=None, reference_path=None) -> None: + """ + Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute" + Used to reference a value of an attribute of an instance through a metric objective entity. + """ + settings = {"metric": metric, "reference_path": reference_path} + + if settings["reference_path"]: + attributes = settings["reference_path"].split(".") + references_created = [] + for i in range(len(attributes)): + if i == 0: + reference = file.create_entity("IfcReference") + reference.AttributeIdentifier = attributes[i] + settings["metric"].ReferencePath = reference + references_created.append(reference) + else: + reference = file.create_entity("IfcReference") + reference.AttributeIdentifier = attributes[i] + references_created[i - 1].InnerReference = reference + references_created.append(reference) + return references_created diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py index 40fb46dfd2..efce0bc080 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py @@ -19,34 +19,31 @@ import ifcopenshell -class Usecase: - def __init__(self, file): - """Add a new objective constraint +def add_objective(file) -> None: + """Add a new objective constraint - Parametric constraints may be defined by the user. The constraint is defined - by first creating an objective describing the purpose of the constraint and - whether it is a hard or soft constraint. Later on, metrics may be added to - check whether the constraint has been met by connecting it to properties and - quantities. See ifcopenshell.api.constraint.add_metric for more information. + Parametric constraints may be defined by the user. The constraint is defined + by first creating an objective describing the purpose of the constraint and + whether it is a hard or soft constraint. Later on, metrics may be added to + check whether the constraint has been met by connecting it to properties and + quantities. See ifcopenshell.api.constraint.add_metric for more information. - :return: The newly created IfcObjective entity - :rtype: ifcopenshell.entity_instance + :return: The newly created IfcObjective entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new objective for code compliance requirements - objective = ifcopenshell.api.run("constraint.add_objective", model) - objective.ConstraintGrade = "ADVISORY" - objective.ObjectiveQualifier = "CODECOMPLIANCE" - # Note: the objective right now is purely qualitative and for - # information purposes. You may wish to add quantiative metrics. - """ - self.file = file - self.settings = {} + # Create a new objective for code compliance requirements + objective = ifcopenshell.api.run("constraint.add_objective", model) + objective.ConstraintGrade = "ADVISORY" + objective.ObjectiveQualifier = "CODECOMPLIANCE" + # Note: the objective right now is purely qualitative and for + # information purposes. You may wish to add quantiative metrics. + """ + settings = {} - def execute(self): - return self.file.create_entity( - "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"} - ) + return file.create_entity( + "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"} + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py index dfc826faf8..89a80d9ab3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py @@ -21,39 +21,41 @@ import ifcopenshell.api from typing import Union +def assign_constraint( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + constraint: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns a constraint to a list of products + + This assigns a relationship between a product and a constraint, so that + when a product's properties and quantities do not match the requirements + of the constraint's metrics, results can be flagged. + + It is assumed (but not explicit in the IFC documentation) that + constraints are inherited from the type. This way, it is not necessary + to create lots of constraint assignments. + + :param products: The list of products the constraint applies to. This is anything + which can have properties or quantities. + :type products: list[ifcopenshell.entity_instance] + :param constraint: The IfcObjective constraint + :type constraint: ifcopenshell.entity_instance + :return: The new or updated IfcRelAssociatesConstraint relationship + or `None` if `products` was an empty list. + :rtype: ifcopenshell.entity_instance + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "constraint": constraint, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - constraint: ifcopenshell.entity_instance, - ): - """Assigns a constraint to a list of products - - This assigns a relationship between a product and a constraint, so that - when a product's properties and quantities do not match the requirements - of the constraint's metrics, results can be flagged. - - It is assumed (but not explicit in the IFC documentation) that - constraints are inherited from the type. This way, it is not necessary - to create lots of constraint assignments. - - :param products: The list of products the constraint applies to. This is anything - which can have properties or quantities. - :type products: list[ifcopenshell.entity_instance] - :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance - :return: The new or updated IfcRelAssociatesConstraint relationship - or `None` if `products` was an empty list. - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "products": products, - "constraint": constraint, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): products = set(self.settings["products"]) if not products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py index 72fead7d88..b1ba5699ca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, metric=None, attributes=None): - """Edit the attributes of a metric +def edit_metric(file, metric=None, attributes=None) -> None: + """Edit the attributes of a metric - For more information about the attributes and data types of an - IfcMetric, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMetric, consult the IFC documentation. - :param metric: The IfcMetric you want to edit. - :type metric: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param metric: The IfcMetric you want to edit. + :type metric: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - ifcopenshell.api.run("constraint.edit_metric", model, - metric=metric, attributes={"ConstraintGrade": "HARD"}) - """ - self.file = file - self.settings = {"metric": metric, "attributes": attributes or {}} + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + ifcopenshell.api.run("constraint.edit_metric", model, + metric=metric, attributes={"ConstraintGrade": "HARD"}) + """ + settings = {"metric": metric, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["metric"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["metric"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py index dff4985539..6ce5c597e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, objective=None, attributes=None): - """Edit the attributes of a objective +def edit_objective(file, objective=None, attributes=None) -> None: + """Edit the attributes of a objective - For more information about the attributes and data types of an - IfcObjective, consult the IFC documentation. + For more information about the attributes and data types of an + IfcObjective, consult the IFC documentation. - :param objective: The IfcObjective you want to edit. - :type objective: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param objective: The IfcObjective you want to edit. + :type objective: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - ifcopenshell.api.run("constraint.edit_objective", model, - objective=objective, attributes={"ConstraintGrade": "HARD"}) - """ - self.file = file - self.settings = {"objective": objective, "attributes": attributes or {}} + objective = ifcopenshell.api.run("constraint.add_objective", model) + ifcopenshell.api.run("constraint.edit_objective", model, + objective=objective, attributes={"ConstraintGrade": "HARD"}) + """ + settings = {"objective": objective, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["objective"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["objective"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py index e7dab1afb0..30b61fb5c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, constraint=None): - """Remove a constraint (typically an objective) +def remove_constraint(file, constraint=None) -> None: + """Remove a constraint (typically an objective) - Removes a constraint definition and all of its associations to any - products. Typically this would be an IfcObjective, although technically - you can associate IfcMetrics ith products too, though the meaning may be - unclear. + Removes a constraint definition and all of its associations to any + products. Typically this would be an IfcObjective, although technically + you can associate IfcMetrics ith products too, though the meaning may be + unclear. - :param constraint: The IfcObjective you want to remove. - :type constraint: ifcopenshell.entity_instance - :return: None - :rtype: None + :param constraint: The IfcObjective you want to remove. + :type constraint: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - ifcopenshell.api.run("constraint.remove_constraint", model, - constraint=objective) - """ - self.file = file - self.settings = {"constraint": constraint} + objective = ifcopenshell.api.run("constraint.add_objective", model) + ifcopenshell.api.run("constraint.remove_constraint", model, + constraint=objective) + """ + settings = {"constraint": constraint} - def execute(self): - self.file.remove(self.settings["constraint"]) - for rel in self.file.by_type("IfcRelAssociatesConstraint"): - if not rel.RelatingConstraint: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + file.remove(settings["constraint"]) + for rel in file.by_type("IfcRelAssociatesConstraint"): + if not rel.RelatingConstraint: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py index 6eaf012fa2..49203da3c5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py @@ -17,31 +17,34 @@ # along with IfcOpenShell. If not, see . +def remove_metric(file, metric=None) -> None: + """Remove a metric benchmark + + Removes a metric benchmark and all of its associations to any products + and objectives. + + :param metric: The IfcMetric you want to remove. + :type metric: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + ifcopenshell.api.run("constraint.remove_metric", model, + metric=metric) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"metric": metric} + return usecase.execute() + + class Usecase: - def __init__(self, file, metric=None): - """Remove a metric benchmark - - Removes a metric benchmark and all of its associations to any products - and objectives. - - :param metric: The IfcMetric you want to remove. - :type metric: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - ifcopenshell.api.run("constraint.remove_metric", model, - metric=metric) - """ - self.file = file - self.settings = {"metric": metric} - def execute(self): if self.settings["metric"].ReferencePath: reference = self.settings["metric"].ReferencePath diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py index 3b6471713e..138964e265 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py @@ -21,31 +21,33 @@ import ifcopenshell.api import ifcopenshell.util.element +def unassign_constraint( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + constraint: ifcopenshell.entity_instance, +) -> None: + """Unassigns a constraint from a list of products + + The constraint will not be deleted and is available to be assigned to + other products. + + :param products: The list of products the constraint applies to. + :type products: list[ifcopenshell.entity_instance] + :param constraint: The IfcObjective constraint + :type constraint: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "constraint": constraint, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - constraint: ifcopenshell.entity_instance, - ): - """Unassigns a constraint from a list of products - - The constraint will not be deleted and is available to be assigned to - other products. - - :param products: The list of products the constraint applies to. - :type products: list[ifcopenshell.entity_instance] - :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "products": products, - "constraint": constraint, - } - def execute(self): products = set(self.settings["products"]) if not products: diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py index e0caddbe3c..1edb3ee252 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_context import add_context +from .edit_context import edit_context +from .remove_context import remove_context diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py index 02156daf0b..95a0929ab6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py @@ -17,168 +17,171 @@ # along with IfcOpenShell. If not, see . +def add_context(file, context_type=None, context_identifier=None, target_view=None, parent=None) -> None: + """Adds a new geometric representation context + + In IFC, physical objects may have zero, one, or multiple geometric + representations associated with it. For example, a building storey might + not have any geometry, but simply be a coordinate in space. + Alternatively, a wall might have a 3D body representation in the form of + a cuboid. As a final example, a door might also have a 3D body + representation of a 3D door panel and door frame, but may additionally + have a 2D door plan view representation of the door swing, and even a 2D + elevation view of the door, a 3D box representing the disabled clearance + zone of the door, a 2D profile representing the profile of the door to + cut out in a wall, and so on. In this situation, a door will have + multiple geometric representations. + + To distinguish between the different purposes of multiple geometric + representations, each geometric representation must belong to a + geometric representation "context". There are typically always 2 + contexts, one for 3D representations and one for 2D representations. + These 2 contexts then have subcontexts for things like the 3D body + representation, clearance representations, annotation representations, + and so on. Each representation of a physical IFC product (e.g. a door) + must be assigned to one of these subcontexts. Therefore setting up + appropriate contexts is critical prior to authoring any IFC model which + contains geometry. + + There are two steps to setting up appropriate subcontexts. First, a 2D + and/or 3D context must be added. These must be always called the "Model" + context for 3D and the "Plan" context for 2D (even if the 2D geometry is + not a plan view). Then, one or more subcontexts are added using either + the "Model" or "Plan" as their parent. These subcontexts are further + distinguished using an "identifier" and "target view". The "identifier" + describes the purpose of the representation, and the "target view" + describes the typical diagrammatic presentation that context's geometry + should be viewed in. The most common identifiers you might use are: + + - Body: for the actual shape of the object + - Box: the bounding box of the object (useful for shape analytics) + - Axis: the parametric line determining the shape of the object + - Profile: the elevation silhouette of the object, useful for cutting + out holes for the object to fit into host elements + - Footprint: the plan view silhouette of the object, useful for certain + quantity take-off rules + - Clearance: the clearance zone of the object + - Annotation: symbolic annotations typically used in diagrams or + drawings + + The most common "target views" you might use are: + + - MODEL_VIEW: for 3D geometry you might see in a BIM viewer + - PLAN_VIEW: for 2D geometry you might see in a plan representation + - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation + - SECTION_VIEW: for 2D geometry you might see in a section representation + - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams + you might use for structural frame analysis, axis-based parametric + modeling + - SKETCH_VIEW: for viewing abstract high-level representations such as + in bubble diagrams of spatial topology + + This may sound like a lot, but after a few typical contexts are set up + at the beginning, it becomes easy to navigate and isolate geometry for + different purposes. There is also the concept of a target scale, which + represents the zoom level detail of geometry, but this is not currently + supported by this API. Setting up all these contexts are also optional, + and you may only use a single Model context and Body subcontext for + simple models, but this simplification sacrifices the ability of more + parametric or analytical usecases. + + :param context_type: The type of the context, must be one of "Model" or + "Plan" only. + :type context_type: str + :param context_identifier: The identifier of the context, chosen from + one of the common identifiers above or consult the IFC documentation + (under the IfcShapeRepresentation page) for more details. Optional + for contexts, but mandatory for subcontexts. + :type context_identifier: str, optional + :param target_view: the target view of the context, chosen from one of + the common target views above or consult the IFC documentation + (under the IfcShapeRepresentation page) for more details. Optional + for contexts, but mandatory for subcontexts. + :type target_view: str, optional + :param parent: the parent context. Must be left as None (the default) + for contexts, and only set for subcontexts. Note that there are only + contexts and subcontexts, a subcontext cannot have any children. + :type parent: ifcopenshell.entity_instance, optional + :return: the newly created IfcGeometricRepresentationContext or + IfcGeometricRepresentationSubContext entity + :rtype: ifcopenshell.entity_instance, optional + + Example: + + .. code:: python + + # If we plan to store 3D geometry in our IFC model, we have to setup + # a "Model" context. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # And/Or, if we plan to store 2D geometry, we need a "Plan" context + plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan") + + # Now we setup the subcontexts with each of the geometric "purposes" + # we plan to store in our model. "Body" is by far the most important + # and common context, as most IFC models are assumed to be viewable + # in 3D. + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # The 3D Axis subcontext is important if any "axis-based" parametric + # geometry is going to be created. For example, a beam, or column + # may be drawn using a single 3D axis line, and for this we need an + # Axis subcontext. + ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d) + + # The 3D Box subcontext is useful for clash detection or shape + # analysis, or even lazy-loading of large models. + ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d) + + # It's also important to have a 2D Axis subcontext for things like + # walls and claddings which can be drawn using a 2D axis line. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan) + + # A 2D annotation subcontext for plan views are important for door + # swings, window cuts, and symbols for equipment like GPOs, fire + # extinguishers, and so on. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan) + + # You may also create 2D annotation subcontexts for sections and + # elevation views. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan) + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context_type": context_type, + "parent": parent, + "context_identifier": context_identifier, + "target_view": target_view, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, context_type=None, context_identifier=None, target_view=None, parent=None): - """Adds a new geometric representation context - - In IFC, physical objects may have zero, one, or multiple geometric - representations associated with it. For example, a building storey might - not have any geometry, but simply be a coordinate in space. - Alternatively, a wall might have a 3D body representation in the form of - a cuboid. As a final example, a door might also have a 3D body - representation of a 3D door panel and door frame, but may additionally - have a 2D door plan view representation of the door swing, and even a 2D - elevation view of the door, a 3D box representing the disabled clearance - zone of the door, a 2D profile representing the profile of the door to - cut out in a wall, and so on. In this situation, a door will have - multiple geometric representations. - - To distinguish between the different purposes of multiple geometric - representations, each geometric representation must belong to a - geometric representation "context". There are typically always 2 - contexts, one for 3D representations and one for 2D representations. - These 2 contexts then have subcontexts for things like the 3D body - representation, clearance representations, annotation representations, - and so on. Each representation of a physical IFC product (e.g. a door) - must be assigned to one of these subcontexts. Therefore setting up - appropriate contexts is critical prior to authoring any IFC model which - contains geometry. - - There are two steps to setting up appropriate subcontexts. First, a 2D - and/or 3D context must be added. These must be always called the "Model" - context for 3D and the "Plan" context for 2D (even if the 2D geometry is - not a plan view). Then, one or more subcontexts are added using either - the "Model" or "Plan" as their parent. These subcontexts are further - distinguished using an "identifier" and "target view". The "identifier" - describes the purpose of the representation, and the "target view" - describes the typical diagrammatic presentation that context's geometry - should be viewed in. The most common identifiers you might use are: - - - Body: for the actual shape of the object - - Box: the bounding box of the object (useful for shape analytics) - - Axis: the parametric line determining the shape of the object - - Profile: the elevation silhouette of the object, useful for cutting - out holes for the object to fit into host elements - - Footprint: the plan view silhouette of the object, useful for certain - quantity take-off rules - - Clearance: the clearance zone of the object - - Annotation: symbolic annotations typically used in diagrams or - drawings - - The most common "target views" you might use are: - - - MODEL_VIEW: for 3D geometry you might see in a BIM viewer - - PLAN_VIEW: for 2D geometry you might see in a plan representation - - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation - - SECTION_VIEW: for 2D geometry you might see in a section representation - - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams - you might use for structural frame analysis, axis-based parametric - modeling - - SKETCH_VIEW: for viewing abstract high-level representations such as - in bubble diagrams of spatial topology - - This may sound like a lot, but after a few typical contexts are set up - at the beginning, it becomes easy to navigate and isolate geometry for - different purposes. There is also the concept of a target scale, which - represents the zoom level detail of geometry, but this is not currently - supported by this API. Setting up all these contexts are also optional, - and you may only use a single Model context and Body subcontext for - simple models, but this simplification sacrifices the ability of more - parametric or analytical usecases. - - :param context_type: The type of the context, must be one of "Model" or - "Plan" only. - :type context_type: str - :param context_identifier: The identifier of the context, chosen from - one of the common identifiers above or consult the IFC documentation - (under the IfcShapeRepresentation page) for more details. Optional - for contexts, but mandatory for subcontexts. - :type context_identifier: str, optional - :param target_view: the target view of the context, chosen from one of - the common target views above or consult the IFC documentation - (under the IfcShapeRepresentation page) for more details. Optional - for contexts, but mandatory for subcontexts. - :type target_view: str, optional - :param parent: the parent context. Must be left as None (the default) - for contexts, and only set for subcontexts. Note that there are only - contexts and subcontexts, a subcontext cannot have any children. - :type parent: ifcopenshell.entity_instance, optional - :return: the newly created IfcGeometricRepresentationContext or - IfcGeometricRepresentationSubContext entity - :rtype: ifcopenshell.entity_instance, optional - - Example: - - .. code:: python - - # If we plan to store 3D geometry in our IFC model, we have to setup - # a "Model" context. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # And/Or, if we plan to store 2D geometry, we need a "Plan" context - plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan") - - # Now we setup the subcontexts with each of the geometric "purposes" - # we plan to store in our model. "Body" is by far the most important - # and common context, as most IFC models are assumed to be viewable - # in 3D. - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # The 3D Axis subcontext is important if any "axis-based" parametric - # geometry is going to be created. For example, a beam, or column - # may be drawn using a single 3D axis line, and for this we need an - # Axis subcontext. - ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d) - - # The 3D Box subcontext is useful for clash detection or shape - # analysis, or even lazy-loading of large models. - ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d) - - # It's also important to have a 2D Axis subcontext for things like - # walls and claddings which can be drawn using a 2D axis line. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan) - - # A 2D annotation subcontext for plan views are important for door - # swings, window cuts, and symbols for equipment like GPOs, fire - # extinguishers, and so on. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan) - - # You may also create 2D annotation subcontexts for sections and - # elevation views. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan) - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = { - "context_type": context_type, - "parent": parent, - "context_identifier": context_identifier, - "target_view": target_view, - } - def execute(self): if not self.settings["parent"]: if self.settings["context_type"] == "Plan": diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py index 50f4612c75..30f6d642b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py @@ -17,37 +17,34 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, context, attributes): - """Edits the attributes of an IfcGeometricRepresentationContext +def edit_context(file, context, attributes) -> None: + """Edits the attributes of an IfcGeometricRepresentationContext - For more information about the attributes and data types of an - IfcGeometricRepresentationContext, consult the IFC documentation. + For more information about the attributes and data types of an + IfcGeometricRepresentationContext, consult the IFC documentation. - :param context: The IfcGeometricRepresentationContext entity you want to edit - :type context: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param context: The IfcGeometricRepresentationContext entity you want to edit + :type context: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - # Revit had a bug where they incorrectly called the body representation a "Facetation" - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model - ) + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + # Revit had a bug where they incorrectly called the body representation a "Facetation" + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model + ) - # Let's fix it! - ifcopenshell.api.run("context.edit_context", model, - context=body, attributes={"ContextIdentifier": "Body"}) - """ - self.file = file - self.settings = {"context": context, "attributes": attributes or {}} + # Let's fix it! + ifcopenshell.api.run("context.edit_context", model, + context=body, attributes={"ContextIdentifier": "Body"}) + """ + settings = {"context": context, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["context"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["context"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py index b0025efbdb..9ac30483cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py @@ -19,49 +19,46 @@ import ifcopenshell -class Usecase: - def __init__(self, file, context=None): - """Removes an IfcGeometricRepresentationContext +def remove_context(file, context=None) -> None: + """Removes an IfcGeometricRepresentationContext - Any representation geometry that is assigned to the context is also - removed. If a context is removed, then any subcontexts are also removed. + Any representation geometry that is assigned to the context is also + removed. If a context is removed, then any subcontexts are also removed. - :param context: The IfcGeometricRepresentationContext entity to remove - :type context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param context: The IfcGeometricRepresentationContext entity to remove + :type context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - # Revit had a bug where they incorrectly called the body representation a "Facetation" - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model - ) + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + # Revit had a bug where they incorrectly called the body representation a "Facetation" + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model + ) - # Let's just get rid of it completely - ifcopenshell.api.run("context.remove_context", model, context=body) - """ - self.file = file - self.settings = {"context": context} + # Let's just get rid of it completely + ifcopenshell.api.run("context.remove_context", model, context=body) + """ + settings = {"context": context} - def execute(self): - for subcontext in self.settings["context"].HasSubContexts: - ifcopenshell.api.run("context.remove_context", self.file, context=subcontext) + for subcontext in settings["context"].HasSubContexts: + ifcopenshell.api.run("context.remove_context", file, context=subcontext) - if getattr(self.settings["context"], "ParentContext", None): - new = self.settings["context"].ParentContext - for inverse in self.file.get_inverse(self.settings["context"]): - if inverse.is_a("IfcCoordinateOperation"): - inverse.SourceCRS = inverse.TargetCRS - ifcopenshell.util.element.remove_deep(self.file, inverse) - else: - ifcopenshell.util.element.replace_attribute(inverse, self.settings["context"], new) - self.file.remove(self.settings["context"]) - else: - representations_in_context = self.settings["context"].RepresentationsInContext - self.file.remove(self.settings["context"]) - for element in representations_in_context: - ifcopenshell.api.run("geometry.remove_representation", self.file, representation=element) + if getattr(settings["context"], "ParentContext", None): + new = settings["context"].ParentContext + for inverse in file.get_inverse(settings["context"]): + if inverse.is_a("IfcCoordinateOperation"): + inverse.SourceCRS = inverse.TargetCRS + ifcopenshell.util.element.remove_deep(file, inverse) + else: + ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new) + file.remove(settings["context"]) + else: + representations_in_context = settings["context"].RepresentationsInContext + file.remove(settings["context"]) + for element in representations_in_context: + ifcopenshell.api.run("geometry.remove_representation", file, representation=element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py index e0caddbe3c..792f5eec35 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py @@ -15,3 +15,6 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_control import assign_control +from .unassign_control import unassign_control diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py index 4d1ecb128d..93a4fe2f35 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -20,87 +20,81 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_control=None, related_object=None): - """Assigns a planning control or constraint to an object +def assign_control(file, relating_control=None, related_object=None) -> None: + """Assigns a planning control or constraint to an object - IFC can describe concepts that control other objects. For example, a - planning calendar controls the availability of working days for - construction planning. As another example, a cost item might constrain - or limit the ability to procure and build a product. + IFC can describe concepts that control other objects. For example, a + planning calendar controls the availability of working days for + construction planning. As another example, a cost item might constrain + or limit the ability to procure and build a product. - This usecase lets you assign controls following the rules of the IFC - specification. This is an advanced topic and assumes knowledge of the - IFC concepts to determine what is allowed to control what. In the - future, this API will likely be deprecated in favour of multiple usecase - specific APIs. + This usecase lets you assign controls following the rules of the IFC + specification. This is an advanced topic and assumes knowledge of the + IFC concepts to determine what is allowed to control what. In the + future, this API will likely be deprecated in favour of multiple usecase + specific APIs. - :param relating_control: The IfcControl entity that is creating the - control or constraint - :type relating_control: ifcopenshell.entity_instance - :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToControl. If relationship already - existed before and wasn't changed then returns None. - :rtype: ifcopenshell.entity_instance, None + :param relating_control: The IfcControl entity that is creating the + control or constraint + :type relating_control: ifcopenshell.entity_instance + :param related_object: The IfcObjectDefinition that is being controlled + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToControl. If relationship already + existed before and wasn't changed then returns None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # One common usecase is to assign a calendar to a task - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model) - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule) + # One common usecase is to assign a calendar to a task + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model) + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule) - # All subtasks will inherit this calendar, so assigning a single - # calendar to the root task effectively defines a "default" calendar - ifcopenshell.api.run("control.assign_control", model, - relating_control=calendar, related_object=task) + # All subtasks will inherit this calendar, so assigning a single + # calendar to the root task effectively defines a "default" calendar + ifcopenshell.api.run("control.assign_control", model, + relating_control=calendar, related_object=task) - # Another common example might be relating a cost item and a product - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - cost_item = ifcopenshell.api.run("cost.add_cost_item", model, - cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=wall) - """ - self.file = file - self.settings = { - "relating_control": relating_control, - "related_object": related_object, - } + # Another common example might be relating a cost item and a product + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + cost_item = ifcopenshell.api.run("cost.add_cost_item", model, + cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=wall) + """ + settings = { + "relating_control": relating_control, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToControl") - and assignment.RelatingControl == self.settings["relating_control"] - ): - return - - controls = None - if self.settings["relating_control"].Controls: - controls = self.settings["relating_control"].Controls[0] - - if controls: - if self.settings["related_object"] in controls.RelatedObjects: + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]: return - related_objects = set(controls.RelatedObjects) - related_objects.add(self.settings["related_object"]) - controls.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": controls}) - else: - controls = self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingControl": self.settings["relating_control"], - }, - ) - return controls + + controls = None + if settings["relating_control"].Controls: + controls = settings["relating_control"].Controls[0] + + if controls: + if settings["related_object"] in controls.RelatedObjects: + return + related_objects = set(controls.RelatedObjects) + related_objects.add(settings["related_object"]) + controls.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": controls}) + else: + controls = file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingControl": settings["relating_control"], + }, + ) + return controls diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py index 72996ad62a..0463689c5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py @@ -21,54 +21,51 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_control=None, related_object=None): - """Unassigns a planning control or constraint to an object +def unassign_control(file, relating_control=None, related_object=None) -> None: + """Unassigns a planning control or constraint to an object - :param relating_control: The IfcControl entity that is creating the - control or constraint - :type relating_control: ifcopenshell.entity_instance - :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance - :return: If the control still is related to other objects, the - IfcRelAssignsToControl is returned, otherwise None. - :rtype: ifcopenshell.entity_instance, None + :param relating_control: The IfcControl entity that is creating the + control or constraint + :type relating_control: ifcopenshell.entity_instance + :param related_object: The IfcObjectDefinition that is being controlled + :type related_object: ifcopenshell.entity_instance + :return: If the control still is related to other objects, the + IfcRelAssignsToControl is returned, otherwise None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # Let's relate a cost item and a product - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - cost_item = ifcopenshell.api.run("cost.add_cost_item", model, - cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=wall) + # Let's relate a cost item and a product + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + cost_item = ifcopenshell.api.run("cost.add_cost_item", model, + cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=wall) - # And now let's change our mind - ifcopenshell.api.run("control.unassign_control", model, - relating_control=cost_item, related_object=wall) - """ + # And now let's change our mind + ifcopenshell.api.run("control.unassign_control", model, + relating_control=cost_item, related_object=wall) + """ - self.file = file - self.settings = { - "relating_control": relating_control, - "related_object": related_object, - } + settings = { + "relating_control": relating_control, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != self.settings["relating_control"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py index e0caddbe3c..4cf5fc63c6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py @@ -15,3 +15,23 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_cost_item import add_cost_item +from .add_cost_item_quantity import add_cost_item_quantity +from .add_cost_schedule import add_cost_schedule +from .add_cost_value import add_cost_value +from .assign_cost_item_quantity import assign_cost_item_quantity +from .assign_cost_value import assign_cost_value +from .calculate_cost_item_resource_value import calculate_cost_item_resource_value +from .copy_cost_item import copy_cost_item +from .copy_cost_item_values import copy_cost_item_values +from .edit_cost_item import edit_cost_item +from .edit_cost_item_quantity import edit_cost_item_quantity +from .edit_cost_schedule import edit_cost_schedule +from .edit_cost_value import edit_cost_value +from .edit_cost_value_formula import edit_cost_value_formula +from .remove_cost_item import remove_cost_item +from .remove_cost_item_quantity import remove_cost_item_quantity +from .remove_cost_schedule import remove_cost_schedule +from .remove_cost_value import remove_cost_value +from .unassign_cost_item_quantity import unassign_cost_item_quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py index 26a0bba442..f270446cfd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py @@ -19,55 +19,52 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_schedule=None, cost_item=None): - """Add a new cost item +def add_cost_item(file, cost_schedule=None, cost_item=None) -> None: + """Add a new cost item - A cost item represents a single line item in a cost schedule. Cost items - may then be broken down into cost subitems. + A cost item represents a single line item in a cost schedule. Cost items + may then be broken down into cost subitems. - :param cost_schedule: If the cost item is to be added as a root or top - level cost item to a cost schedule, the IfcCostSchedule may be - specified. This is mutually exlclusive to the cost_item parameter. - :type cost_schedule: ifcopenshell.entity_instance - :param cost_item: If the cost item is to be added as a subitem to an - existing cost item, the parent IfcCostItem may be specified. This is - mutually exclusive to the cost_schedule parameter. - :type cost_item: ifcopenshell.entity_instance - :return: The newly created IfcCostItem - :rtype: ifcopenshell.entity_instance + :param cost_schedule: If the cost item is to be added as a root or top + level cost item to a cost schedule, the IfcCostSchedule may be + specified. This is mutually exlclusive to the cost_item parameter. + :type cost_schedule: ifcopenshell.entity_instance + :param cost_item: If the cost item is to be added as a subitem to an + existing cost item, the parent IfcCostItem may be specified. This is + mutually exclusive to the cost_schedule parameter. + :type cost_item: ifcopenshell.entity_instance + :return: The newly created IfcCostItem + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # The very first cost item must be in a cost schedule - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # The very first cost item must be in a cost schedule + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # You may add cost items as top level item in the schedule - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # You may add cost items as top level item in the schedule + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Alternatively you may add them as subitems - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1) - """ - self.file = file - self.settings = {"cost_schedule": cost_schedule, "cost_item": cost_item} + # Alternatively you may add them as subitems + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1) + """ + settings = {"cost_schedule": cost_schedule, "cost_item": cost_item} - def execute(self): - cost_item = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCostItem") + cost_item = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcCostItem") - if self.settings["cost_schedule"]: - self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [cost_item], - "RelatingControl": self.settings["cost_schedule"], - } - ) - elif self.settings["cost_item"]: - ifcopenshell.api.run( - "nest.assign_object", self.file, related_objects=[cost_item], relating_object=self.settings["cost_item"] - ) - return cost_item + if settings["cost_schedule"]: + file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [cost_item], + "RelatingControl": settings["cost_schedule"], + } + ) + elif settings["cost_item"]: + ifcopenshell.api.run( + "nest.assign_object", file, related_objects=[cost_item], relating_object=settings["cost_item"] + ) + return cost_item diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index fabe443460..47a9b3efb9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -19,73 +19,70 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_item=None, ifc_class="IfcQuantityCount"): - """Adds a new quantity associated with a cost item +def add_cost_item_quantity(file, cost_item=None, ifc_class="IfcQuantityCount") -> None: + """Adds a new quantity associated with a cost item - Cost items calculate their subtotal by multiplying the sum of the cost - item's "values" by the sum of the cost item's "quantities". The - quantities may be either parametrically linked to quantities measured on - physical product, or manually specified. + Cost items calculate their subtotal by multiplying the sum of the cost + item's "values" by the sum of the cost item's "quantities". The + quantities may be either parametrically linked to quantities measured on + physical product, or manually specified. - The quantity must be of a particular type, common examples are: + The quantity must be of a particular type, common examples are: - - IfcQuantityCount: to count the total occurrences of a product, useful - for things like doors, windows, and furniture - - IfcQuantityNumber: any other generic numeric quantity - - IfcQuantityLength - - IfcQuantityArea - - IfcQuantityVolume - - IfcQuantityWeight - - IfcQuantityTime + - IfcQuantityCount: to count the total occurrences of a product, useful + for things like doors, windows, and furniture + - IfcQuantityNumber: any other generic numeric quantity + - IfcQuantityLength + - IfcQuantityArea + - IfcQuantityVolume + - IfcQuantityWeight + - IfcQuantityTime - A cost item must not mix quantities of different types. + A cost item must not mix quantities of different types. - If an IfcQuantityCount is used, then this API will automatically count - all products that this cost item controls (see - ifcopenshell.api.controls.assign_control) and prefill that quantity. + If an IfcQuantityCount is used, then this API will automatically count + all products that this cost item controls (see + ifcopenshell.api.controls.assign_control) and prefill that quantity. - For all other quantity types, the quantity is left as zero and the user - must either manually specify the quantity or parametrically link it - using another API call. + For all other quantity types, the quantity is left as zero and the user + must either manually specify the quantity or parametrically link it + using another API call. - :param cost_item: The IfcCostItem to add the quantity to - :type cost_item: ifcopenshell.entity_instance - :param ifc_class: The type of quantity to add - :type ifc_class: str, optional - :return: The newly created quantity entity, chosen from the ifc_class - parameter - :rtype: ifcopenshell.entity_instance + :param cost_item: The IfcCostItem to add the quantity to + :type cost_item: ifcopenshell.entity_instance + :param ifc_class: The type of quantity to add + :type ifc_class: str, optional + :return: The newly created quantity entity, chosen from the ifc_class + parameter + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=chair) + chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=chair) - # Let's assume we want to count the amount of chairs to calculate our cost item - # Because this is an IfcQuantityCount the count will be automatically set to "1" chair - ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityCount") - """ - self.file = file - self.settings = {"cost_item": cost_item, "ifc_class": ifc_class} + # Let's assume we want to count the amount of chairs to calculate our cost item + # Because this is an IfcQuantityCount the count will be automatically set to "1" chair + ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityCount") + """ + settings = {"cost_item": cost_item, "ifc_class": ifc_class} - def execute(self): - quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") - quantity[3] = 0.0 - # This is a bold assumption - # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 - if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls: - count = 0 - for rel in self.settings["cost_item"].Controls: - count += len(rel.RelatedObjects) - quantity[3] = count - quantities = list(self.settings["cost_item"].CostQuantities or []) - quantities.append(quantity) - self.settings["cost_item"].CostQuantities = quantities - return quantity + quantity = file.create_entity(settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if settings["ifc_class"] == "IfcQuantityCount" and settings["cost_item"].Controls: + count = 0 + for rel in settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count + quantities = list(settings["cost_item"].CostQuantities or []) + quantities.append(quantity) + settings["cost_item"].CostQuantities = quantities + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py index fa97a893f3..d72566ae1f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py @@ -21,48 +21,45 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__(self, file, name=None, predefined_type="NOTDEFINED"): - """Add a new cost schedule +def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None: + """Add a new cost schedule - A cost schedule is a group of cost items which typically represent a - cost plan or breakdown of the project. This may be used as an estimate, - bid, or actual cost. + A cost schedule is a group of cost items which typically represent a + cost plan or breakdown of the project. This may be used as an estimate, + bid, or actual cost. - Alternatively, a cost schedule may also represent a schedule of rates, - which include cost items which capture unit rates for different elements - or processes. + Alternatively, a cost schedule may also represent a schedule of rates, + which include cost items which capture unit rates for different elements + or processes. - As such, creating a cost schedule is necessary prior to creating and - managing any cost items. + As such, creating a cost schedule is necessary prior to creating and + managing any cost items. - :param name: The name of the cost schedule. - :type name: str, optional - :param predefined_type: The predefined type of the cost schedule, chosen - from a valid type in the IFC documentation for - IfcCostScheduleTypeEnum - :type predefined_type: str, optional - :return: The newly created IfcCostSchedule entity - :rtype: ifcopenshell.entity_instance + :param name: The name of the cost schedule. + :type name: str, optional + :param predefined_type: The predefined type of the cost schedule, chosen + from a valid type in the IFC documentation for + IfcCostScheduleTypeEnum + :type predefined_type: str, optional + :return: The newly created IfcCostSchedule entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # Now that we have a cost schedule, we may add cost items to it - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - """ - self.file = file - self.settings = {"name": name, "predefined_type": predefined_type} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # Now that we have a cost schedule, we may add cost items to it + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + """ + settings = {"name": name, "predefined_type": predefined_type} - def execute(self): - cost_schedule = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcCostSchedule", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") - return cost_schedule + cost_schedule = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcCostSchedule", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + return cost_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py index e7a481e8b2..b6fe5f5698 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py @@ -17,95 +17,92 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None): - """Adds a new value or subvalue to a cost item +def add_cost_value(file, parent=None) -> None: + """Adds a new value or subvalue to a cost item - A cost item's subtotal can be specified in two ways. + A cost item's subtotal can be specified in two ways. - Option 1 is by simply manually specifying the subtotal value, which - represents the full cost of that cost item. This option occurs when a - cost item has no quantities associated with it. + Option 1 is by simply manually specifying the subtotal value, which + represents the full cost of that cost item. This option occurs when a + cost item has no quantities associated with it. - Option 2 is by specifying a unit cost value of the cost item, which is - then multiplied by the associated quantity of the cost item, to give us - the subtotal. This option occurs when a cost item has quantities - associated with it. + Option 2 is by specifying a unit cost value of the cost item, which is + then multiplied by the associated quantity of the cost item, to give us + the subtotal. This option occurs when a cost item has quantities + associated with it. - For either option 1 (full cost value) or option 2 (unit cost value), the - cost value may be specified as a single number, or as a sum of - subcomponents or formulas (e.g. multiplication by wastage factor, or - adding taxes or other adjustments). + For either option 1 (full cost value) or option 2 (unit cost value), the + cost value may be specified as a single number, or as a sum of + subcomponents or formulas (e.g. multiplication by wastage factor, or + adding taxes or other adjustments). - This function lets you add a single top level unit value to a cost item, - or alternatively price subcomponents by using the "parent" parameter. + This function lets you add a single top level unit value to a cost item, + or alternatively price subcomponents by using the "parent" parameter. - More advanced usage, which involves summing, subcategory-filtered costs, - and formulas are possible but not yet documented. + More advanced usage, which involves summing, subcategory-filtered costs, + and formulas are possible but not yet documented. - :param parent: A parent IfcCostItem, if specifying a price directly to a - cost item, or a top-level price component. Alternatively, this can - be set to a IfcCostValue, if specifying price subcomponents. - :type parent: ifcopenshell.entity_instance - :return: The newly created IfcCostValue - :rtype: ifcopenshell.entity_instance + :param parent: A parent IfcCostItem, if specifying a price directly to a + cost item, or a top-level price component. Alternatively, this can + be set to a IfcCostValue, if specifying price subcomponents. + :type parent: ifcopenshell.entity_instance + :return: The newly created IfcCostValue + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We always need a schedule first prior to adding any cost items - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # We always need a schedule first prior to adding any cost items + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # Option 1: This cost item will have a full cost of 42.0 - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) + # Option 1: This cost item will have a full cost of 42.0 + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) - # Option 2: This cost item will have a unit cost of 5.0 per unit - # area, multiplied by the quantity of area specified explicitly as - # 3.0, giving us a subtotal cost of 15.0. - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item2, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("cost.edit_cost_item_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) + # Option 2: This cost item will have a unit cost of 5.0 per unit + # area, multiplied by the quantity of area specified explicitly as + # 3.0, giving us a subtotal cost of 15.0. + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item2, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("cost.edit_cost_item_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) - # A cost value may also be specified in terms of the sum of its - # subcomponents. In this case, it's broken down into 2 subvalues. - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) - subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) - subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) + # A cost value may also be specified in terms of the sum of its + # subcomponents. In this case, it's broken down into 2 subvalues. + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) + subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) + subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) - # This specifies that the value is the sum of all subitems - # regardless of their cost category. The first subvalue is 2.0 and - # the second is 3.0, giving a total value of 5.0. - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"}) - ifcopenshell.api.run("cost.edit_cost_value", model, - cost_value=subvalue1, attributes={"AppliedValue": 2.0}) - ifcopenshell.api.run("cost.edit_cost_value", model, - cost_value=subvalue2, attributes={"AppliedValue": 3.0}) - """ - self.file = file - self.settings = {"parent": parent} + # This specifies that the value is the sum of all subitems + # regardless of their cost category. The first subvalue is 2.0 and + # the second is 3.0, giving a total value of 5.0. + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"}) + ifcopenshell.api.run("cost.edit_cost_value", model, + cost_value=subvalue1, attributes={"AppliedValue": 2.0}) + ifcopenshell.api.run("cost.edit_cost_value", model, + cost_value=subvalue2, attributes={"AppliedValue": 3.0}) + """ + settings = {"parent": parent} - def execute(self): - value = self.file.create_entity("IfcCostValue") - if self.settings["parent"].is_a("IfcCostItem"): - values = list(self.settings["parent"].CostValues or []) - values.append(value) - self.settings["parent"].CostValues = values - elif self.settings["parent"].is_a("IfcConstructionResource"): - values = list(self.settings["parent"].BaseCosts or []) - values.append(value) - self.settings["parent"].BaseCosts = values - elif self.settings["parent"].is_a("IfcCostValue"): - values = list(self.settings["parent"].Components or []) - values.append(value) - self.settings["parent"].Components = values - return value + value = file.create_entity("IfcCostValue") + if settings["parent"].is_a("IfcCostItem"): + values = list(settings["parent"].CostValues or []) + values.append(value) + settings["parent"].CostValues = values + elif settings["parent"].is_a("IfcConstructionResource"): + values = list(settings["parent"].BaseCosts or []) + values.append(value) + settings["parent"].BaseCosts = values + elif settings["parent"].is_a("IfcCostValue"): + values = list(settings["parent"].Components or []) + values.append(value) + settings["parent"].Components = values + return value diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index d4c6b6be69..6c13162ed7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -19,82 +19,82 @@ import ifcopenshell.api +def assign_cost_item_quantity(file, cost_item=None, products=None, prop_name="") -> None: + """Adds a cost item quantity that is parametrically connected to a product + + A cost item may have its subtotal calculated by multiplying a unit value + by a quantity associated with the cost item. That quantity may be either + manually specified or parametrically connected to a quantity on a + product. This API function lets you create that parametric connection. + + For example, you may wish to have a cost item linked to the "NetVolume" + quantity on all IfcSlabs. Each quantity has a name which you can + specify. If the quantity is updated in-place (which should occur for + Native IFC applications) then the quantity for the cost item will + automatically update as well. If the quantity is deleted and then + re-added, then the parametric relationship is also lost. + + This API also automatically assigns a control relationship between the + cost item and the product, so it is not necessary to use + ifcopenshell.api.control.assign_control. + + :param cost_item: The IfcCostItem to assign parametric quantities to + :type cost_item: ifcopenshell.entity_instance + :param products: The IfcObjects to assign parametric quantities to + :type products: list[ifcopenshell.entity_instance] + :param prop_name: The name of the quantity. If this is not specified, + then it is assumed that there is no calculated quantity, and the + number of objects are counted instead. + :type prop_name: str, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + # Let's imagine a unit cost of 5.0 per unit volume + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + + slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") + # Usually the quantity would be automatically calculated via a + # graphical authoring application but let's assign a manual quantity + # for now. + qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) + + # Now let's parametrically link the slab's quantity to the cost + # item. If the slab is edited in the future and 42.0 changes, then + # the updated value will also automatically be applied to the cost + # item. + ifcopenshell.api.run("cost.assign_cost_item_quantity", model, + cost_item=item, products=[slab], prop_name="NetVolume") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "cost_item": cost_item, + "products": products or [], + "prop_name": prop_name, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None, products=None, prop_name=""): - """Adds a cost item quantity that is parametrically connected to a product - - A cost item may have its subtotal calculated by multiplying a unit value - by a quantity associated with the cost item. That quantity may be either - manually specified or parametrically connected to a quantity on a - product. This API function lets you create that parametric connection. - - For example, you may wish to have a cost item linked to the "NetVolume" - quantity on all IfcSlabs. Each quantity has a name which you can - specify. If the quantity is updated in-place (which should occur for - Native IFC applications) then the quantity for the cost item will - automatically update as well. If the quantity is deleted and then - re-added, then the parametric relationship is also lost. - - This API also automatically assigns a control relationship between the - cost item and the product, so it is not necessary to use - ifcopenshell.api.control.assign_control. - - :param cost_item: The IfcCostItem to assign parametric quantities to - :type cost_item: ifcopenshell.entity_instance - :param products: The IfcObjects to assign parametric quantities to - :type products: list[ifcopenshell.entity_instance] - :param prop_name: The name of the quantity. If this is not specified, - then it is assumed that there is no calculated quantity, and the - number of objects are counted instead. - :type prop_name: str, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - # Let's imagine a unit cost of 5.0 per unit volume - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - - slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") - # Usually the quantity would be automatically calculated via a - # graphical authoring application but let's assign a manual quantity - # for now. - qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) - - # Now let's parametrically link the slab's quantity to the cost - # item. If the slab is edited in the future and 42.0 changes, then - # the updated value will also automatically be applied to the cost - # item. - ifcopenshell.api.run("cost.assign_cost_item_quantity", model, - cost_item=item, products=[slab], prop_name="NetVolume") - """ - self.file = file - self.settings = { - "cost_item": cost_item, - "products": products or [], - "prop_name": prop_name, - } - def execute(self): if self.settings["prop_name"]: self.quantities = set(self.settings["cost_item"].CostQuantities or []) for product in self.settings["products"]: - self.assign_cost_control( - related_object=product, cost_item=self.settings["cost_item"] - ) + self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"]) if self.settings["prop_name"]: if ( self.settings["cost_item"].CostQuantities - and self.settings["cost_item"].CostQuantities[0].Name.lower() - != self.settings["prop_name"].lower() + and self.settings["cost_item"].CostQuantities[0].Name.lower() != self.settings["prop_name"].lower() ) or not product.is_a("IfcObject"): continue self.add_quantity_from_related_object(product) @@ -120,10 +120,7 @@ class Usecase: if not qto.is_a("IfcElementQuantity"): return for prop in qto.Quantities: - if ( - prop.is_a("IfcPhysicalSimpleQuantity") - and prop.Name.lower() == self.settings["prop_name"].lower() - ): + if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): self.quantities.add(prop) def update_cost_item_count(self): diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py index fb89fe7432..18bb05694f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py @@ -19,60 +19,57 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_item=None, cost_rate=None): - """Assigns a cost value to a cost item from a schedule of rates +def assign_cost_value(file, cost_item=None, cost_rate=None) -> None: + """Assigns a cost value to a cost item from a schedule of rates - Instead of assigning cost values from scratch for each cost item in a - cost schedule, the cost values may instead be assigned from a schedule - of rates. + Instead of assigning cost values from scratch for each cost item in a + cost schedule, the cost values may instead be assigned from a schedule + of rates. - A schedule of rates is just another cost schedule which have cost values - but no quantities. This API will allow you to "copy" the values from a - cost item in the schedule of rates into another cost item in your own - cost schedule. When the schedule of rates value is updated, then your - cost item values will also be updated. You can think of the schedule of - rates as a "template" to quickly populate your rates from. + A schedule of rates is just another cost schedule which have cost values + but no quantities. This API will allow you to "copy" the values from a + cost item in the schedule of rates into another cost item in your own + cost schedule. When the schedule of rates value is updated, then your + cost item values will also be updated. You can think of the schedule of + rates as a "template" to quickly populate your rates from. - :param cost_item: The IfcCostItem that you want to copy the values to - :type cost_item: ifcopenshell.entity_instance - :param cost_rate: The IfcCostItem that you want to copy the values from - :type cost_rate: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem that you want to copy the values to + :type cost_item: ifcopenshell.entity_instance + :param cost_rate: The IfcCostItem that you want to copy the values from + :type cost_rate: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a schedule of rates with a single rate in it of 5.0 - rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model, - predefined_type="SCHEDULEOFRATES") - rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) + # Let's create a schedule of rates with a single rate in it of 5.0 + rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model, + predefined_type="SCHEDULEOFRATES") + rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) - # And this schedule will be for our actual cost plan / estimate / etc - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # And this schedule will be for our actual cost plan / estimate / etc + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Now the cost item has the same rate as the one from the schedule of rate's item - ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate) - """ - self.file = file - self.settings = {"cost_item": cost_item, "cost_rate": cost_rate} + # Now the cost item has the same rate as the one from the schedule of rate's item + ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate) + """ + settings = {"cost_item": cost_item, "cost_rate": cost_rate} - def execute(self): - if self.settings["cost_item"].CostValues: - [ - ifcopenshell.api.run( - "cost.remove_cost_value", - self.file, - parent=self.settings["cost_item"], - cost_value=cost_value, - ) - for cost_value in self.settings["cost_item"].CostValues - ] - # This is an assumption, and not part of the official IFC documentation - self.settings["cost_item"].CostValues = self.settings["cost_rate"].CostValues + if settings["cost_item"].CostValues: + [ + ifcopenshell.api.run( + "cost.remove_cost_value", + file, + parent=settings["cost_item"], + cost_value=cost_value, + ) + for cost_value in settings["cost_item"].CostValues + ] + # This is an assumption, and not part of the official IFC documentation + settings["cost_item"].CostValues = settings["cost_rate"].CostValues diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py index b82e5948a6..c977be1b19 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py @@ -21,100 +21,97 @@ import ifcopenshell.util.date import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, cost_item=None): - """Calculates the total cost of all resources associated with a cost item +def calculate_cost_item_resource_value(file, cost_item=None) -> None: + """Calculates the total cost of all resources associated with a cost item - A cost item may have construction resources (e.g. equipment, material, - etc) assigned to it. Construction resources may be assigned directly to - the cost item, or assigned first to a task, and the task is then - assigned to the cost item. + A cost item may have construction resources (e.g. equipment, material, + etc) assigned to it. Construction resources may be assigned directly to + the cost item, or assigned first to a task, and the task is then + assigned to the cost item. - The cost of a resource is calculated by the total sum of all of its base - costs. If no quantity is provided, that sum is considered to be the - total cost. Otherwise, it is considered to be a unit cost, and is then - multiplied by the resource quantity. The quantity is either stored as a - base quantity (such as a volume) for a things like material resources, - or as a duration as a daily rate for labour resources. + The cost of a resource is calculated by the total sum of all of its base + costs. If no quantity is provided, that sum is considered to be the + total cost. Otherwise, it is considered to be a unit cost, and is then + multiplied by the resource quantity. The quantity is either stored as a + base quantity (such as a volume) for a things like material resources, + or as a duration as a daily rate for labour resources. - The final calculated cost is set as the cost item's value. Any - previously existing values are removed. + The final calculated cost is set as the cost item's value. Any + previously existing values are removed. - :param cost_item: The IfcCostItem to calculate - :type cost_item: ifccopenshell.entity_instance.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem to calculate + :type cost_item: ifccopenshell.entity_instance.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # First, we need a cost schedule and item - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # First, we need a cost schedule and item + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Let's imagine we have our own formworking crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Let's imagine we have our own formworking crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # ... and they need concrete - concrete = ifcopenshell.api.run("resource.add_resource", model, - ifc_class="IfcConstructionMaterialResource", parent_resource=crew) - ifcopenshell.api.run("control.assign_control", model, - relating_control=item, related_object=concrete) - # ... which has a unit price of 42.0 per m3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) - # ... and a volume of 200m3 - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=concrete, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 200.0}) + # ... and they need concrete + concrete = ifcopenshell.api.run("resource.add_resource", model, + ifc_class="IfcConstructionMaterialResource", parent_resource=crew) + ifcopenshell.api.run("control.assign_control", model, + relating_control=item, related_object=concrete) + # ... which has a unit price of 42.0 per m3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) + # ... and a volume of 200m3 + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=concrete, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 200.0}) - # Let's say they also need some equipment - equipment = ifcopenshell.api.run("resource.add_resource", model, - ifc_class="IfcConstructionEquipmentResource", parent_resource=crew) - ifcopenshell.api.run("control.assign_control", model, - relating_control=item, related_object=equipment) - # ... with a fixed price of 50,000 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) + # Let's say they also need some equipment + equipment = ifcopenshell.api.run("resource.add_resource", model, + ifc_class="IfcConstructionEquipmentResource", parent_resource=crew) + ifcopenshell.api.run("control.assign_control", model, + relating_control=item, related_object=equipment) + # ... with a fixed price of 50,000 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) - # (42 * 200) + 50000 = 58400 is our calculated cost - ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item) - """ - self.file = file - self.settings = {"cost_item": cost_item} + # (42 * 200) + 50000 = 58400 is our calculated cost + ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item) + """ + settings = {"cost_item": cost_item} - def execute(self): - for cost_value in self.settings["cost_item"].CostValues or []: - ifcopenshell.api.run( - "cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value - ) + for cost_value in settings["cost_item"].CostValues or []: + ifcopenshell.api.run("cost.remove_cost_value", file, parent=settings["cost_item"], cost_value=cost_value) - resources = [] - for rel in self.settings["cost_item"].Controls or []: - for related_object in rel.RelatedObjects: - if related_object.is_a("IfcConstructionResource"): - resources.append(related_object) - elif related_object.is_a("IfcTask"): - for rel2 in related_object.OperatesOn or []: - for related_object2 in rel2.RelatedObjects: - if related_object2.is_a("IfcConstructionResource"): - resources.append(related_object2) + resources = [] + for rel in settings["cost_item"].Controls or []: + for related_object in rel.RelatedObjects: + if related_object.is_a("IfcConstructionResource"): + resources.append(related_object) + elif related_object.is_a("IfcTask"): + for rel2 in related_object.OperatesOn or []: + for related_object2 in rel2.RelatedObjects: + if related_object2.is_a("IfcConstructionResource"): + resources.append(related_object2) - for resource in resources: - cost, unit = ifcopenshell.util.resource.get_cost(resource) - if not cost: - cost, unit = ifcopenshell.util.resource.get_parent_cost(resource) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data. - quantity = ifcopenshell.util.resource.get_quantity(resource) - if not cost or not quantity: - continue - if unit and "day" in unit: - quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar - quantity = round(quantity, 2) - formula = "{}*{}".format(cost, quantity) - cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"]) - cost_value.Name = resource.Name - ifcopenshell.api.run("cost.edit_cost_value_formula", self.file, cost_value=cost_value, formula=formula) \ No newline at end of file + for resource in resources: + cost, unit = ifcopenshell.util.resource.get_cost(resource) + if not cost: + cost, unit = ifcopenshell.util.resource.get_parent_cost( + resource + ) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data. + quantity = ifcopenshell.util.resource.get_quantity(resource) + if not cost or not quantity: + continue + if unit and "day" in unit: + quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar + quantity = round(quantity, 2) + formula = "{}*{}".format(cost, quantity) + cost_value = ifcopenshell.api.run("cost.add_cost_value", file, parent=settings["cost_item"]) + cost_value.Name = resource.Name + ifcopenshell.api.run("cost.edit_cost_value_formula", file, cost_value=cost_value, formula=formula) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py index ec2d147731..13927088ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py @@ -21,35 +21,38 @@ import ifcopenshell.api import ifcopenshell.util.element +def copy_cost_item(file, cost_item=None) -> None: + """Copies all cost items and related relationships + + The following relationships are also duplicated: + + * The copy will have the same attributes and property sets as the original cost item + * The copy will be assigned to the parent cost schedule + * The copy will have duplicated nested cost items + + :param cost_item: The cost item to be duplicated + :type cost_item: ifcopenshell.entity_instance + :return: The duplicated cost item or the list of duplicated cost items if the latter has children + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a cost item + cost_item = CostItem(name="Design new feature", deadline="2023-03-01") + + # And now we have two + duplicated_cost_item = project.duplicate_cost_item(cost_item) + + + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_item": cost_item} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None): - """Copies all cost items and related relationships - - The following relationships are also duplicated: - - * The copy will have the same attributes and property sets as the original cost item - * The copy will be assigned to the parent cost schedule - * The copy will have duplicated nested cost items - - :param cost_item: The cost item to be duplicated - :type cost_item: ifcopenshell.entity_instance - :return: The duplicated cost item or the list of duplicated cost items if the latter has children - :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a cost item - cost_item = CostItem(name="Design new feature", deadline="2023-03-01") - - # And now we have two - duplicated_cost_item = project.duplicate_cost_item(cost_item) - - - """ - self.file = file - self.settings = {"cost_item": cost_item} - def execute(self): self.new_cost_items = [] self.duplicate_cost_item(self.settings["cost_item"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py index 6bc0677b28..8ccb0a4158 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py @@ -20,45 +20,42 @@ import ifcopenshell.util.element import ifcopenshell.api -class Usecase: - def __init__(self, file, source=None, destination=None): - """Copies all cost values from one cost item to another +def copy_cost_item_values(file, source=None, destination=None) -> None: + """Copies all cost values from one cost item to another - Any previously existing values will be removed. The entire value is - copied, including all components and formulas. However they are not - parametrically linked, so if one value changes, the other will not. + Any previously existing values will be removed. The entire value is + copied, including all components and formulas. However they are not + parametrically linked, so if one value changes, the other will not. - :param source: The IfcCostItem to copy cost values from - :type source: ifcopenshell.entity_instance - :param destination: The IfcCostItem to copy cost values from - :type destination: ifcopenshell.entity_instance - :return: None - :rtype: None + :param source: The IfcCostItem to copy cost values from + :type source: ifcopenshell.entity_instance + :param destination: The IfcCostItem to copy cost values from + :type destination: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Assume we have a schedule with multiple items in it - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # Assume we have a schedule with multiple items in it + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # One of the items has a value - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5000.0}) + # One of the items has a value + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5000.0}) - # Let's copy the value from one item to another - ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2) - """ - self.file = file - self.settings = {"source": source, "destination": destination} + # Let's copy the value from one item to another + ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2) + """ + settings = {"source": source, "destination": destination} - def execute(self): - for cost_value in self.settings["destination"].CostValues or []: - ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=cost_value) - copied_cost_values = [] - for cost_value in self.settings["source"].CostValues or []: - copied_cost_values.append(ifcopenshell.util.element.copy_deep(self.file, cost_value)) - self.settings["destination"].CostValues = copied_cost_values + for cost_value in settings["destination"].CostValues or []: + ifcopenshell.api.run("cost.remove_cost_item_value", file, cost_value=cost_value) + copied_cost_values = [] + for cost_value in settings["source"].CostValues or []: + copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value)) + settings["destination"].CostValues = copied_cost_values diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py index cc0a187177..2bf72a5d57 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_item=None, attributes=None): - """Edits the attributes of an IfcCostItem +def edit_cost_item(file, cost_item=None, attributes=None) -> None: + """Edits the attributes of an IfcCostItem - For more information about the attributes and data types of an - IfcCostItem, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostItem, consult the IFC documentation. - :param cost_item: The IfcCostItem entity you want to edit - :type cost_item: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_item: The IfcCostItem entity you want to edit + :type cost_item: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"cost_item": cost_item, "attributes": attributes or {}} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"}) + """ + settings = {"cost_item": cost_item, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["cost_item"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["cost_item"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py index 3ba4e9f498..178816a593 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py @@ -17,39 +17,36 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, physical_quantity=None, attributes=None): - """Edits the attributes of an IfcPhysicalQuantity +def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> None: + """Edits the attributes of an IfcPhysicalQuantity - For more information about the attributes and data types of an - IfcPhysicalQuantity, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPhysicalQuantity, consult the IFC documentation. - :param physical_quantity: The IfcPhysicalQuantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param physical_quantity: The IfcPhysicalQuantity entity you want to edit + :type physical_quantity: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a unit cost of 5 and a volume of 3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("cost.edit_cost_item_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) - """ - self.file = file - self.settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}} + # This cost item will have a unit cost of 5 and a volume of 3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("cost.edit_cost_item_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) + """ + settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["physical_quantity"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py index bdfb856cc1..3e47f3a430 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_schedule=None, attributes=None): - """Edits the attributes of an IfcCostSchedule +def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None: + """Edits the attributes of an IfcCostSchedule - For more information about the attributes and data types of an - IfcCostSchedule, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostSchedule, consult the IFC documentation. - :param cost_schedule: The IfcCostSchedule entity you want to edit - :type cost_schedule: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_schedule: The IfcCostSchedule entity you want to edit + :type cost_schedule: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - ifcopenshell.api.run("cost.edit_cost_schedule", model, - cost_schedule=schedule, attributes={"Name": "Foo"}) - """ + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + ifcopenshell.api.run("cost.edit_cost_schedule", model, + cost_schedule=schedule, attributes={"Name": "Foo"}) + """ - self.file = file - self.settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}} + settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["cost_schedule"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["cost_schedule"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py index 75ce055eb1..430b4272aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py @@ -21,48 +21,45 @@ import ifcopenshell.util.unit import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_value=None, attributes=None): - """Edits the attributes of an IfcCostValue +def edit_cost_value(file, cost_value=None, attributes=None) -> None: + """Edits the attributes of an IfcCostValue - For more information about the attributes and data types of an - IfcCostValue, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostValue, consult the IFC documentation. - :param cost_value: The IfcCostValue entity you want to edit - :type cost_value: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_value: The IfcCostValue entity you want to edit + :type cost_value: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a total cost of 42 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) - """ - self.file = file - self.settings = {"cost_value": cost_value, "attributes": attributes or {}} + # This cost item will have a total cost of 42 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) + """ + settings = {"cost_value": cost_value, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "AppliedValue" and value is not None: - # TODO: support all applied value select types - value = self.file.createIfcMonetaryMeasure(value) - elif name == "UnitBasis": - old_unit_basis = self.settings["cost_value"].UnitBasis - if value: - value_component = self.file.create_entity( - ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), - value["ValueComponent"], - ) - value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"]) - if old_unit_basis and len(self.file.get_inverse(old_unit_basis)) == 0: - ifcopenshell.util.element.remove_deep(self.file, old_unit_basis) - setattr(self.settings["cost_value"], name, value) + for name, value in settings["attributes"].items(): + if name == "AppliedValue" and value is not None: + # TODO: support all applied value select types + value = file.createIfcMonetaryMeasure(value) + elif name == "UnitBasis": + old_unit_basis = settings["cost_value"].UnitBasis + if value: + value_component = file.create_entity( + ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), + value["ValueComponent"], + ) + value = file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"]) + if old_unit_basis and len(file.get_inverse(old_unit_basis)) == 0: + ifcopenshell.util.element.remove_deep(file, old_unit_basis) + setattr(settings["cost_value"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py index 8dada5dc98..eac443ba40 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py @@ -22,37 +22,40 @@ import ifcopenshell.util.unit import ifcopenshell.util.element +def edit_cost_value_formula(file, cost_value=None, formula=None) -> None: + """Sets a cost value based on a formula, similar to formulas in spreadsheets + + Costs may be made up of many components (e.g. labour, material, waste + factor, taxes, etc). This can be easily represented in the form of a + formula similar thta would be used in spreadsheet applications. + + For more information, see ifcopenshell.util.cost + + :param cost_value: The IfcCostValue to set the values of + :type cost_value: ifcopenshell.entity_instance + :param formula: The formula following the language of ifcopenshell.util.cost + :type formula: str + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value, + formula="5000 * 1.19") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_value": cost_value, "formula": formula or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_value=None, formula=None): - """Sets a cost value based on a formula, similar to formulas in spreadsheets - - Costs may be made up of many components (e.g. labour, material, waste - factor, taxes, etc). This can be easily represented in the form of a - formula similar thta would be used in spreadsheet applications. - - For more information, see ifcopenshell.util.cost - - :param cost_value: The IfcCostValue to set the values of - :type cost_value: ifcopenshell.entity_instance - :param formula: The formula following the language of ifcopenshell.util.cost - :type formula: str - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value, - formula="5000 * 1.19") - """ - self.file = file - self.settings = {"cost_value": cost_value, "formula": formula or {}} - def execute(self): try: data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py index 5596ceff13..e52fd655cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py @@ -21,48 +21,45 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_item=None): - """Removes a cost item +def remove_cost_item(file, cost_item=None) -> None: + """Removes a cost item - All associated relationships with the cost item are also removed, - however the related resources, products, and tasks themselves are - retained. + All associated relationships with the cost item are also removed, + however the related resources, products, and tasks themselves are + retained. - :param cost_item: The IfcCostItem entity you want to remove - :type cost_item: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem entity you want to remove + :type cost_item: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item) - """ - self.file = file - self.settings = {"cost_item": cost_item} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item) + """ + settings = {"cost_item": cost_item} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["cost_item"]): - if inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["cost_item"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run("cost.remove_cost_item", self.file, cost_item=related_object) - elif inverse.RelatedObjects == (self.settings["cost_item"],): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToControl"): + # TODO: do a deep purge + for inverse in file.get_inverse(settings["cost_item"]): + if inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["cost_item"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object) + elif inverse.RelatedObjects == (settings["cost_item"],): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["cost_item"].OwnerHistory - self.file.remove(self.settings["cost_item"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToControl"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["cost_item"].OwnerHistory + file.remove(settings["cost_item"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py index fae8e1cd37..eed3a7adb3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_item=None, physical_quantity=None): - """Removes a quantity assigned to a cost item +def remove_cost_item_quantity(file, cost_item=None, physical_quantity=None) -> None: + """Removes a quantity assigned to a cost item - If the quantity is part of a product (e.g. wall), then the quantity will - still exist and merely the relationship to the cost item will be - removed. + If the quantity is part of a product (e.g. wall), then the quantity will + still exist and merely the relationship to the cost item will be + removed. - :param cost_item: The IfcCostItem that the quantity is assigned to - :type cost_item: ifcopenshell.entity_instance - :param physical_quantity: The IfcPhysicalQuantity to remove - :type physical_quantity: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem that the quantity is assigned to + :type cost_item: ifcopenshell.entity_instance + :param physical_quantity: The IfcPhysicalQuantity to remove + :type physical_quantity: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityVolume") - # Let's change our mind and delete it - ifcopenshell.api.run("cost.remove_cost_item", model, - cost_item=item, physical_quantity=quantity) - """ - self.file = file - self.settings = {"cost_item": cost_item, "physical_quantity": physical_quantity} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityVolume") + # Let's change our mind and delete it + ifcopenshell.api.run("cost.remove_cost_item", model, + cost_item=item, physical_quantity=quantity) + """ + settings = {"cost_item": cost_item, "physical_quantity": physical_quantity} - def execute(self): - if len(self.file.get_inverse(self.settings["physical_quantity"])) == 1: - self.file.remove(self.settings["physical_quantity"]) - return - quantities = list(self.settings["cost_item"].CostQuantities or []) - quantities.remove(self.settings["physical_quantity"]) - self.settings["cost_item"].CostQuantities = quantities + if len(file.get_inverse(settings["physical_quantity"])) == 1: + file.remove(settings["physical_quantity"]) + return + quantities = list(settings["cost_item"].CostQuantities or []) + quantities.remove(settings["physical_quantity"]) + settings["cost_item"].CostQuantities = quantities diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py index 51feebb76e..7b73859bb0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py @@ -21,41 +21,36 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_schedule=None): - """Removes a cost schedule +def remove_cost_schedule(file, cost_schedule=None) -> None: + """Removes a cost schedule - All associated relationships with the cost schedule are also removed, - including all cost items. + All associated relationships with the cost schedule are also removed, + including all cost items. - :param cost_schedule: The IfcCostSchedule entity you want to remove - :type cost_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_schedule: The IfcCostSchedule entity you want to remove + :type cost_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule) - """ - self.file = file - self.settings = {"cost_schedule": cost_schedule} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule) + """ + settings = {"cost_schedule": cost_schedule} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["cost_schedule"]): - if inverse.is_a("IfcRelAssignsToControl"): - [ - ifcopenshell.api.run( - "cost.remove_cost_item", self.file, cost_item=related_object - ) - for related_object in inverse.RelatedObjects - if related_object.is_a("IfcCostItem") - ] - history = self.settings["cost_schedule"].OwnerHistory - self.file.remove(self.settings["cost_schedule"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for inverse in file.get_inverse(settings["cost_schedule"]): + if inverse.is_a("IfcRelAssignsToControl"): + [ + ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object) + for related_object in inverse.RelatedObjects + if related_object.is_a("IfcCostItem") + ] + history = settings["cost_schedule"].OwnerHistory + file.remove(settings["cost_schedule"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py index 4af7322899..757877bf9d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py @@ -17,51 +17,48 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None, cost_value=None): - """Removes a cost value +def remove_cost_value(file, parent=None, cost_value=None) -> None: + """Removes a cost value - The cost value may be assigned either to a cost item, a construction - resource, or another cost value (i.e. it is a subcomponent of a cost) + The cost value may be assigned either to a cost item, a construction + resource, or another cost value (i.e. it is a subcomponent of a cost) - :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue - that the IfcCostValue is assigned to. - :type parent: ifcopenshell.entity_instance - :param cost_value: The IfcCostValue that you want to remove - :type parent: ifcopenshell.entity_instance - :return: None - :rtype: None + :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue + that the IfcCostValue is assigned to. + :type parent: ifcopenshell.entity_instance + :param cost_value: The IfcCostValue that you want to remove + :type parent: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a unit cost of 5 and a volume of 3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) + # This cost item will have a unit cost of 5 and a volume of 3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) - ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value) - """ - self.file = file - self.settings = {"parent": parent, "cost_value": cost_value} + ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value) + """ + settings = {"parent": parent, "cost_value": cost_value} - def execute(self): - if len(self.file.get_inverse(self.settings["cost_value"])) == 1: - self.file.remove(self.settings["cost_value"]) - # TODO deep purge - elif self.settings["parent"].is_a("IfcCostItem"): - values = list(self.settings["parent"].CostValues) - values.remove(self.settings["cost_value"]) - self.settings["parent"].CostValues = values if values else None - elif self.settings["parent"].is_a("IfcConstructionResource"): - values = list(self.settings["parent"].BaseCosts) - values.remove(self.settings["cost_value"]) - self.settings["parent"].BaseCosts = values if values else None - elif self.settings["parent"].is_a("IfcCostValue"): - components = list(self.settings["parent"].Components) - components.remove(self.settings["cost_value"]) - self.settings["parent"].Components = components if components else None + if len(file.get_inverse(settings["cost_value"])) == 1: + file.remove(settings["cost_value"]) + # TODO deep purge + elif settings["parent"].is_a("IfcCostItem"): + values = list(settings["parent"].CostValues) + values.remove(settings["cost_value"]) + settings["parent"].CostValues = values if values else None + elif settings["parent"].is_a("IfcConstructionResource"): + values = list(settings["parent"].BaseCosts) + values.remove(settings["cost_value"]) + settings["parent"].BaseCosts = values if values else None + elif settings["parent"].is_a("IfcCostValue"): + components = list(settings["parent"].Components) + components.remove(settings["cost_value"]) + settings["parent"].Components = components if components else None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py index 091029f594..c7c5fc69fd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py @@ -19,56 +19,59 @@ import ifcopenshell.api +def unassign_cost_item_quantity(file, cost_item=None, products=None) -> None: + """Removes quantities of a cost item that are calculated on products + + A cost item may have quantities that are parametrically calculated on + physical products. This lets you remove those quantities. This means + that any future changes in the physical product's dimensions will not + have any impact on the cost item. + + :param cost_item: The IfcCostItem to remove quantities from + :type cost_item: ifcopenshell.entity_instance + :param products: A list of IfcProducts that may have parametrically + connected quantities to the cost item + :type products: list[ifcopenshell.entity_instance] + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + # Let's imagine a unit cost of 5.0 per unit volume + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + + slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") + # Usually the quantity would be automatically calculated via a + # graphical authoring application but let's assign a manual quantity + # for now. + qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) + + # Now let's parametrically link the slab's quantity to the cost + # item. If the slab is edited in the future and 42.0 changes, then + # the updated value will also automatically be applied to the cost + # item. + ifcopenshell.api.run("cost.assign_cost_item_quantity", model, + cost_item=item, products=[slab], prop_name="NetVolume") + + # Let's change our mind and remove the parametric connection + ifcopenshell.api.run("cost.unassign_cost_item_quantity", model, + cost_item=item, products=[slab]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_item": cost_item, "products": products or []} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None, products=None): - """Removes quantities of a cost item that are calculated on products - - A cost item may have quantities that are parametrically calculated on - physical products. This lets you remove those quantities. This means - that any future changes in the physical product's dimensions will not - have any impact on the cost item. - - :param cost_item: The IfcCostItem to remove quantities from - :type cost_item: ifcopenshell.entity_instance - :param products: A list of IfcProducts that may have parametrically - connected quantities to the cost item - :type products: list[ifcopenshell.entity_instance] - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - # Let's imagine a unit cost of 5.0 per unit volume - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - - slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") - # Usually the quantity would be automatically calculated via a - # graphical authoring application but let's assign a manual quantity - # for now. - qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) - - # Now let's parametrically link the slab's quantity to the cost - # item. If the slab is edited in the future and 42.0 changes, then - # the updated value will also automatically be applied to the cost - # item. - ifcopenshell.api.run("cost.assign_cost_item_quantity", model, - cost_item=item, products=[slab], prop_name="NetVolume") - - # Let's change our mind and remove the parametric connection - ifcopenshell.api.run("cost.unassign_cost_item_quantity", model, - cost_item=item, products=[slab]) - """ - self.file = file - self.settings = {"cost_item": cost_item, "products": products or []} - def execute(self): self.quantities = set(self.settings["cost_item"].CostQuantities or []) for quantity in self.settings["cost_item"].CostQuantities or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py index e0caddbe3c..b1affe3a71 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py @@ -15,3 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_information import add_information +from .add_reference import add_reference +from .assign_document import assign_document +from .edit_information import edit_information +from .edit_reference import edit_reference +from .remove_information import remove_information +from .remove_reference import remove_reference +from .unassign_document import unassign_document diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py index 68c9c53759..fc60134477 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py @@ -19,69 +19,62 @@ import ifcopenshell -class Usecase: - def __init__(self, file, parent=None): - """Adds a new document information to the project +def add_information(file, parent=None) -> None: + """Adds a new document information to the project - An IFC document information is a document associated with the project. - It may be a drawing, specification, schedule, certificate, warranty - guarantee, manual, contract, and so on. They are often used for drawings - and facility management purposes. + An IFC document information is a document associated with the project. + It may be a drawing, specification, schedule, certificate, warranty + guarantee, manual, contract, and so on. They are often used for drawings + and facility management purposes. - A document may also be a subdocument of a larger document, this is - useful for superseding documents or tracking older versions. The parent - is considered the latest version and the children are older revisions. + A document may also be a subdocument of a larger document, this is + useful for superseding documents or tracking older versions. The parent + is considered the latest version and the children are older revisions. - :param parent: The parent document, if necessary. - :type parent: ifcopenshell.entity_instance, optional - :return: The newly created IfcDocumentInformation entity - :rtype: ifcopenshell.entity_instance + :param parent: The parent document, if necessary. + :type parent: ifcopenshell.entity_instance, optional + :return: The newly created IfcDocumentInformation entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - # A document typically has a unique drawing or document name (which - # follows a coding system depending on the project), as well as a - # title. This should match what is shown on the titleblock or title - # page of the document. At a minimum you'd also want to specify a - # URI location. The location may be on local, or on a CDE, or any - # other platform. - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - """ - self.file = file - self.settings = {"parent": parent} + document = ifcopenshell.api.run("document.add_information", model) + # A document typically has a unique drawing or document name (which + # follows a coding system depending on the project), as well as a + # title. This should match what is shown on the titleblock or title + # page of the document. At a minimum you'd also want to specify a + # URI location. The location may be on local, or on a CDE, or any + # other platform. + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + """ + settings = {"parent": parent} - def execute(self): - id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification" - information = self.file.create_entity( - "IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"} + id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification" + information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"}) + parent = settings["parent"] + if not parent and file.by_type("IfcProject"): + parent = file.by_type("IfcProject")[0] + if parent.is_a("IfcProject") or parent.is_a("IfcContext"): + file.create_entity( + "IfcRelAssociatesDocument", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatingDocument=information, + RelatedObjects=[parent], ) - parent = self.settings["parent"] - if not parent and self.file.by_type("IfcProject"): - parent = self.file.by_type("IfcProject")[0] - if parent.is_a("IfcProject") or parent.is_a("IfcContext"): - self.file.create_entity( - "IfcRelAssociatesDocument", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatingDocument=information, - RelatedObjects=[parent], + elif parent.is_a("IfcDocumentInformation"): + if parent.IsPointer: + rel = parent.IsPointer[0] + documents = set(rel.RelatedDocuments) + documents.add(information) + rel.RelatedDocuments = list(documents) + else: + file.create_entity( + "IfcDocumentInformationRelationship", RelatingDocument=parent, RelatedDocuments=[information] ) - elif parent.is_a("IfcDocumentInformation"): - if parent.IsPointer: - rel = parent.IsPointer[0] - documents = set(rel.RelatedDocuments) - documents.add(information) - rel.RelatedDocuments = list(documents) - else: - self.file.create_entity( - "IfcDocumentInformationRelationship", - RelatingDocument=parent, - RelatedDocuments=[information] - ) - return information + return information diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py index 80cf91d8a1..3b96b6d666 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py @@ -19,62 +19,57 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, information: ifcopenshell.entity_instance): - """Creates a new reference to a document to assign to products +def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: + """Creates a new reference to a document to assign to products - A document may be associated with physical products, tasks, cost items, - and so on. For example, spaces, storeys, and buildings may have a list - of associated drawings so you can see which drawings (e.g. plans, - sections, details) are documenting that location. Alternatively, - equipment may have associated training manuals, operation and - maintenance manuals or detailed assembly drawings. Resources may be - training certification required, schedules may have gantt charts or bid - documents, and so on. + A document may be associated with physical products, tasks, cost items, + and so on. For example, spaces, storeys, and buildings may have a list + of associated drawings so you can see which drawings (e.g. plans, + sections, details) are documenting that location. Alternatively, + equipment may have associated training manuals, operation and + maintenance manuals or detailed assembly drawings. Resources may be + training certification required, schedules may have gantt charts or bid + documents, and so on. - In order to associate a document with an object, a reference to that - document needs to be created. It could be a reference to the entire - document, or a reference to a particular page or chapter. See - ifcopenshell.api.document.assign_document for more information. + In order to associate a document with an object, a reference to that + document needs to be created. It could be a reference to the entire + document, or a reference to a particular page or chapter. See + ifcopenshell.api.document.assign_document for more information. - :param information: The IfcDocumentInformation that the reference will - be created for - :type information: ifcopenshell.entity_instance - :return: The newly created IfcDocumentReference entity - :rtype: ifcopenshell.entity_instance + :param information: The IfcDocumentInformation that the reference will + be created for + :type information: ifcopenshell.entity_instance + :return: The newly created IfcDocumentReference entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) - # In this case, we don't specify any more information, and so the - # reference is for the entire document, as opposed to a single page or - # chapter or section. - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + # In this case, we don't specify any more information, and so the + # reference is for the entire document, as opposed to a single page or + # chapter or section. + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Alternatively, we can specify a single section, such as by a - # subheading code. - reference2 = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.edit_reference", model, - reference=reference2, attributes={"Identification": "2.1.15"}) - """ - self.file = file - self.settings = {"information": information} + # Alternatively, we can specify a single section, such as by a + # subheading code. + reference2 = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.edit_reference", model, + reference=reference2, attributes={"Identification": "2.1.15"}) + """ + settings = {"information": information} - def execute(self) -> ifcopenshell.entity_instance: - if self.file.schema == "IFC2X3": - reference = self.file.create_entity("IfcDocumentReference", ItemReference="X") - if self.settings["information"]: - references = list(self.settings["information"].DocumentReferences or []) - references.append(reference) - self.settings["information"].DocumentReferences = references - return reference - return self.file.create_entity( - "IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X" - ) + if file.schema == "IFC2X3": + reference = file.create_entity("IfcDocumentReference", ItemReference="X") + if settings["information"]: + references = list(settings["information"].DocumentReferences or []) + references.append(reference) + settings["information"].DocumentReferences = references + return reference + return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X") diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py index f9b433213a..5818347a90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py @@ -22,93 +22,85 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - document: ifcopenshell.entity_instance, - ): - """Assigns a document to a list of products +def assign_document( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + document: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns a document to a list of products - An object may be assigned to zero, one, or multiple documents. Almost - any object or property may be assigned to a document, though typically - we'd only use it for spaces, types, physical products and schedules. - Adding a new assignment is typically done using a document reference and - an object. IFC technically allows association with a document - information and an object, but this is not encouraged because it is not - consistent with other external relationships (such as classification - systems or libraries). + An object may be assigned to zero, one, or multiple documents. Almost + any object or property may be assigned to a document, though typically + we'd only use it for spaces, types, physical products and schedules. + Adding a new assignment is typically done using a document reference and + an object. IFC technically allows association with a document + information and an object, but this is not encouraged because it is not + consistent with other external relationships (such as classification + systems or libraries). - :param product: The list of objects to associate the document to. This could be - almost any sensible object in IFC. - :type product: list[ifcopenshell.entity_instance] - :param document: The IfcDocumentReference to associate to, or - alternatively an IfcDocumentInformation, though this is not - recommended. - :type document: ifcopenshell.entity_instance - :return: The IfcRelAssociatesDocument relationship - or `None` if `products` was an empty list or all products were - already assigned to the `document`. - :rtype: ifcopenshell.entity_instance + :param product: The list of objects to associate the document to. This could be + almost any sensible object in IFC. + :type product: list[ifcopenshell.entity_instance] + :param document: The IfcDocumentReference to associate to, or + alternatively an IfcDocumentInformation, though this is not + recommended. + :type document: ifcopenshell.entity_instance + :return: The IfcRelAssociatesDocument relationship + or `None` if `products` was an empty list or all products were + already assigned to the `document`. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Let's imagine storey represents an IfcBuildingStorey for the ground floor - ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) - """ - self.file = file - self.settings = { - "products": products, - "document": document, - } + # Let's imagine storey represents an IfcBuildingStorey for the ground floor + ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) + """ + settings = { + "products": products, + "document": document, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - # NOTE: reuses code from `library.assign_reference` + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # NOTE: reuses code from `library.assign_reference` - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"]) - products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) - products = products - referenced_elements + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["document"]) + products: set[ifcopenshell.entity_instance] = set(settings["products"]) + products = products - referenced_elements - if not products: - return + if not products: + return - if self.file.schema == "IFC2X3": - rel = next( - ( - r - for r in self.file.by_type("IfcRelAssociatesDocument") - if r.RelatingDocument == self.settings["document"] - ), - None, - ) - else: - ifc_class = self.settings["document"].is_a() - if ifc_class == "IfcDocumentReference": - rel = next(iter(self.settings["document"].DocumentRefForObjects), None) - elif ifc_class == "IfcDocumentInformation": - rel = next(iter(self.settings["document"].DocumentInfoForObjects), None) + if file.schema == "IFC2X3": + rel = next( + (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == settings["document"]), + None, + ) + else: + ifc_class = settings["document"].is_a() + if ifc_class == "IfcDocumentReference": + rel = next(iter(settings["document"].DocumentRefForObjects), None) + elif ifc_class == "IfcDocumentInformation": + rel = next(iter(settings["document"].DocumentInfoForObjects), None) - if not rel: - return self.file.create_entity( - "IfcRelAssociatesDocument", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatedObjects=list(products), - RelatingDocument=self.settings["document"], - ) + if not rel: + return file.create_entity( + "IfcRelAssociatesDocument", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatedObjects=list(products), + RelatingDocument=settings["document"], + ) - related_objects = set(rel.RelatedObjects) | products - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + related_objects = set(rel.RelatedObjects) | products + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py index 96c0120120..478c1c11da 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py @@ -19,38 +19,34 @@ import ifcopenshell from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - information: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcDocumentInformation +def edit_information( + file: ifcopenshell.file, + information: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcDocumentInformation - For more information about the attributes and data types of an - IfcDocumentInformation, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDocumentInformation, consult the IFC documentation. - :param reference: The IfcDocumentInformation entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcDocumentInformation entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - """ - self.file = file - self.settings = {"information": information, "attributes": attributes or {}} + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + """ + settings = {"information": information, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - setattr(self.settings["information"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["information"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py index d88afdfc2f..fb705fbbc2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py @@ -19,41 +19,37 @@ import ifcopenshell from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcDocumentReference +def edit_reference( + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcDocumentReference - For more information about the attributes and data types of an - IfcDocumentReference, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDocumentReference, consult the IFC documentation. - :param reference: The IfcDocumentReference entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcDocumentReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.edit_reference", model, - reference=reference, attributes={"Identification": "2.1.15"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.edit_reference", model, + reference=reference, attributes={"Identification": "2.1.15"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py index 56f57df283..86531252e9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py @@ -22,45 +22,42 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, information=None): - """Removes a document information +def remove_information(file, information=None) -> None: + """Removes a document information - All references and associations are also removed. + All references and associations are also removed. - :param information: The IfcDocumentInformation to remove - :type information: ifcopenshell.entity_instance - :return: None - :rtype: None + :param information: The IfcDocumentInformation to remove + :type information: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add a document - document = ifcopenshell.api.run("document.add_information", model) - # ... and remove it! - ifcopenshell.api.run("document.remove_information", model, information=document) - """ - self.file = file - self.settings = {"information": information} + # Add a document + document = ifcopenshell.api.run("document.add_information", model) + # ... and remove it! + ifcopenshell.api.run("document.remove_information", model, information=document) + """ + settings = {"information": information} - def execute(self): - for reference in self.settings["information"].HasDocumentReferences or []: - ifcopenshell.api.run("document.remove_reference", self.file, reference=reference) + for reference in settings["information"].HasDocumentReferences or []: + ifcopenshell.api.run("document.remove_reference", file, reference=reference) - for rel in self.settings["information"].IsPointer or []: - for information in rel.RelatedDocuments: - ifcopenshell.api.run("document.remove_information", self.file, information=information) + for rel in settings["information"].IsPointer or []: + for information in rel.RelatedDocuments: + ifcopenshell.api.run("document.remove_information", file, information=information) - for rel in self.settings["information"].IsPointedTo or []: - if rel.RelatedDocuments == (self.settings["information"],): - # This relationship is non-rooted - self.file.remove(rel) + for rel in settings["information"].IsPointedTo or []: + if rel.RelatedDocuments == (settings["information"],): + # This relationship is non-rooted + file.remove(rel) - for rel in self.settings["information"].DocumentInfoForObjects or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["information"]) + for rel in settings["information"].DocumentInfoForObjects or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["information"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py index 61fd6810c1..5321b480f7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py @@ -20,32 +20,29 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance): - """Remove a document reference +def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None: + """Remove a document reference - All associations with objects are removed. + All associations with objects are removed. - :param reference: The IfcDocumentReference to remove - :type reference: ifcopenshell.entity_instance - :return: None - :rtype: None + :param reference: The IfcDocumentReference to remove + :type reference: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.remove_reference", model, reference=reference) - """ - self.file = file - self.settings = {"reference": reference} + document = ifcopenshell.api.run("document.add_information", model) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.remove_reference", model, reference=reference) + """ + settings = {"reference": reference} - def execute(self) -> None: - for rel in self.settings["reference"].DocumentRefForObjects or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["reference"]) + for rel in settings["reference"].DocumentRefForObjects or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py index c4728d5511..44c0543501 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py @@ -21,69 +21,65 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - document: ifcopenshell.entity_instance, - ): - """Unassigns a document and an association to the list of products +def unassign_document( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + document: ifcopenshell.entity_instance, +) -> None: + """Unassigns a document and an association to the list of products - :param product: The list of objects that the document reference or information is - related to. - :type product: list[ifcopenshell.entity_instance] - :param document: The IfcDocumentReference (typically) or in rare cases - the IfcDocumentInformation that is associated with the product - :type document: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The list of objects that the document reference or information is + related to. + :type product: list[ifcopenshell.entity_instance] + :param document: The IfcDocumentReference (typically) or in rare cases + the IfcDocumentInformation that is associated with the product + :type document: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Let's imagine storey represents an IfcBuildingStorey for the ground floor - ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) + # Let's imagine storey represents an IfcBuildingStorey for the ground floor + ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) - # Now let's change our mind and remove the association - ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference) - """ - self.file = file - self.settings = { - "products": products, - "document": document, - } + # Now let's change our mind and remove the association + ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference) + """ + settings = { + "products": products, + "document": document, + } - def execute(self): - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - # NOTE: reuses code from `library.un assign_reference` + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # NOTE: reuses code from `library.un assign_reference` - reference_rels: set[ifcopenshell.entity_instance] = set() - products = set(self.settings["products"]) - for product in products: - reference_rels.update(product.HasAssociations) + reference_rels: set[ifcopenshell.entity_instance] = set() + products = set(settings["products"]) + for product in products: + reference_rels.update(product.HasAssociations) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"] - } + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"] + } - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py index e0caddbe3c..dd010e886e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_product import assign_product +from .edit_text_literal import edit_text_literal +from .unassign_product import unassign_product diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py index 051dd985b3..b35d0bd564 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py @@ -20,95 +20,92 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Associates a product and an object, typically for annotation +def assign_product(file, relating_product=None, related_object=None) -> None: + """Associates a product and an object, typically for annotation - Warning: this is an experimental API. + Warning: this is an experimental API. - When you want to draw attention to a feature or characteristic (such as - a dimension, material, or name) or of a product (e.g. wall, slab, - furniture, etc), an annotation object is created. This annotation is - then associated with the product so that it can reference attributes, - properties, and relationships. + When you want to draw attention to a feature or characteristic (such as + a dimension, material, or name) or of a product (e.g. wall, slab, + furniture, etc), an annotation object is created. This annotation is + then associated with the product so that it can reference attributes, + properties, and relationships. - For example, an annotation of a line will be associated with a grid - axis, such that when that grid axis moves, the annotation of that grid - axis (which is typically truncated to the extents of a drawing) will - also move. + For example, an annotation of a line will be associated with a grid + axis, such that when that grid axis moves, the annotation of that grid + axis (which is typically truncated to the extents of a drawing) will + also move. - Another example might be a label of a furniture product, which might - have some text of the name of the furniture to be shown on drawings or - in 3D. + Another example might be a label of a furniture product, which might + have some text of the name of the furniture to be shown on drawings or + in 3D. - :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance - :param related_object: The object (typically IfcAnnotation) that the - product is related to - :type related_object: ifcopenshell.entity_instance - :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct the object is related to + :type relating_product: ifcopenshell.entity_instance + :param related_object: The object (typically IfcAnnotation) that the + product is related to + :type related_object: ifcopenshell.entity_instance + :return: The created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") - ifcopenshell.api.run("drawing.assign_product", model, - relating_product=furniture, related_object=annotation) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") + ifcopenshell.api.run("drawing.assign_product", model, + relating_product=furniture, related_object=annotation) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis") + is_grid_axis = settings["relating_product"].is_a("IfcGridAxis") - if is_grid_axis: - if self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToProduct") and rel.Name == self.settings["relating_product"].AxisTag: - return - elif self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == self.settings["relating_product"]: + if is_grid_axis: + if settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag: return + elif settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]: + return - referenced_by = None + referenced_by = None - if is_grid_axis: - axis = self.settings["relating_product"] - grid = None - for attribute in ("PartOfW", "PartOfV", "PartOfU"): - if getattr(axis, attribute, None): - grid = getattr(axis, attribute)[0] - self.settings["relating_product"] = grid - for rel in grid.ReferencedBy: - if rel.Name == axis.AxisTag: - referenced_by = rel - break - elif self.settings["relating_product"].ReferencedBy: - referenced_by = self.settings["relating_product"].ReferencedBy[0] + if is_grid_axis: + axis = settings["relating_product"] + grid = None + for attribute in ("PartOfW", "PartOfV", "PartOfU"): + if getattr(axis, attribute, None): + grid = getattr(axis, attribute)[0] + settings["relating_product"] = grid + for rel in grid.ReferencedBy: + if rel.Name == axis.AxisTag: + referenced_by = rel + break + elif settings["relating_product"].ReferencedBy: + referenced_by = settings["relating_product"].ReferencedBy[0] - if referenced_by: - related_objects = list(referenced_by.RelatedObjects) - related_objects.append(self.settings["related_object"]) - referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by}) - else: - referenced_by = self.file.create_entity( - "IfcRelAssignsToProduct", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProduct": self.settings["relating_product"], - } - ) + if referenced_by: + related_objects = list(referenced_by.RelatedObjects) + related_objects.append(settings["related_object"]) + referenced_by.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by}) + else: + referenced_by = file.create_entity( + "IfcRelAssignsToProduct", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProduct": settings["relating_product"], + } + ) - if is_grid_axis: - referenced_by.Name = axis.AxisTag - return referenced_by + if is_grid_axis: + referenced_by.Name = axis.AxisTag + return referenced_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py index 00b8bc4f82..f1aadc25b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, text_literal=None, attributes=None): - """Edits the attributes of an IfcTextLiteral +def edit_text_literal(file, text_literal=None, attributes=None) -> None: + """Edits the attributes of an IfcTextLiteral - For more information about the attributes and data types of an - IfcTextLiteral, consult the IFC documentation. + For more information about the attributes and data types of an + IfcTextLiteral, consult the IFC documentation. - :param reference: The IfcTextLiteral entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcTextLiteral entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - text = model.createIfcTextLiteral() - ifcopenshell.api.run("drawing.edit_text_literal", model, - text_literal=text, attributes={"Literal": "MY ANNOTATION"}) - """ - self.file = file - self.settings = {"text_literal": text_literal, "attributes": attributes or {}} + text = model.createIfcTextLiteral() + ifcopenshell.api.run("drawing.edit_text_literal", model, + text_literal=text, attributes={"Literal": "MY ANNOTATION"}) + """ + settings = {"text_literal": text_literal, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["text_literal"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["text_literal"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py index 91ee7ded3d..8254bffdce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py @@ -21,54 +21,51 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Unassigns a product and an object (typically an annotation) +def unassign_product(file, relating_product=None, related_object=None) -> None: + """Unassigns a product and an object (typically an annotation) - Smart annotation objects can be associated with products so that they - can annotate attributes and properties. This function lets you remove - the association, so that you may change the assocation with another - object later or leave the annotation as a "dumb" annotation. + Smart annotation objects can be associated with products so that they + can annotate attributes and properties. This function lets you remove + the association, so that you may change the assocation with another + object later or leave the annotation as a "dumb" annotation. - :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance - :param related_object: The object (typically IfcAnnotation) that the - product is related to - :type related_object: ifcopenshell.entity_instance - :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct the object is related to + :type relating_product: ifcopenshell.entity_instance + :param related_object: The object (typically IfcAnnotation) that the + product is related to + :type related_object: ifcopenshell.entity_instance + :return: The created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") - ifcopenshell.api.run("drawing.assign_product", model, - relating_product=furniture, related_object=annotation) + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") + ifcopenshell.api.run("drawing.assign_product", model, + relating_product=furniture, related_object=annotation) - # Let's change our mind and remove the relationship - ifcopenshell.api.run("drawing.unassign_product", model, - relating_product=furniture, related_object=annotation) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Let's change our mind and remove the relationship + ifcopenshell.api.run("drawing.unassign_product", model, + relating_product=furniture, related_object=annotation) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py index e0caddbe3c..1caaa312ba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py @@ -15,3 +15,30 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_axis_representation import add_axis_representation +from .add_boolean import add_boolean +from .add_door_representation import add_door_representation +from .add_footprint_representation import add_footprint_representation +from .add_mesh_representation import add_mesh_representation +from .add_profile_representation import add_profile_representation +from .add_railing_representation import add_railing_representation + +try: + from .add_representation import add_representation +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}") +from .add_slab_representation import add_slab_representation +from .add_wall_representation import add_wall_representation +from .add_window_representation import add_window_representation +from .assign_representation import assign_representation +from .connect_element import connect_element +from .connect_path import connect_path +from .create_2pt_wall import create_2pt_wall +from .disconnect_element import disconnect_element +from .disconnect_path import disconnect_path +from .edit_object_placement import edit_object_placement +from .map_representation import map_representation +from .remove_boolean import remove_boolean +from .remove_representation import remove_representation +from .unassign_representation import unassign_representation diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py index 8a09531a26..d8180d287f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py @@ -19,61 +19,64 @@ import ifcopenshell.util.unit +def add_axis_representation(file, context=None, axis=None) -> None: + """Adds a new axis representation + + Certain objects are typically "axis-based", such as walls, beams, + and columns. This means you can represent them abstractly by simply + drawing a single line either in 2D (such as for walls) or 3D (for beams + and columns). Humans can understand this axis-based representation as + being a simplification of a layered extrusion or a profile that is being + extruded along that axis and joined to other elements. + + Using an axis-based representation makes it easy for users and computers + to analyse connectivity and spatial relationships, as well as makes it + easy to parametrically edit these objects by simply stretching the start + or end of the axis. + + For now, only simple straight line axes are supported, represented by a + start and end coordinate. The order is important. For walls, the start + must be at the minimum local X ordinate, and the end at the maximum + local X ordinate. For beams and columns, the start is at the minimum + local Z ordinate, and the end of the maximum local Z ordinate. The first + coordinate is the "start" and the second coordinate is the "end". This + stat and end is then used to determine any parametric junctions with + other elements. + + Using an axis-representation is optional, but highly recommended for + "standard" representations of walls, beams, columns, and other + structural members. A rule of thumb is that if you can draw it as a line + on paper, you can probably represent it using an axis. + + :param context: The IfcGeometricRepresentationContext that the + representation is part of. This must be either a + Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D). + :type context: ifcopenshell.entity_instance + :param axis: The axis, as a list of two coordinates, the coordinates + being either a list of 2 or 3 float coordinates depending on whether + the axis is 2D or 3D. + :type axis: list[list[float]] + :return: The newly created IfcShapeRepresentation entity + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW") + axis = ifcopenshell.api.run("geometry.add_axis_representation", model, + context=context, axis=[(0.0, 0.0), (1.0, 0.0)]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": context, + "axis": axis or [], + } + return usecase.execute() + + class Usecase: - def __init__(self, file, context=None, axis=None): - """Adds a new axis representation - - Certain objects are typically "axis-based", such as walls, beams, - and columns. This means you can represent them abstractly by simply - drawing a single line either in 2D (such as for walls) or 3D (for beams - and columns). Humans can understand this axis-based representation as - being a simplification of a layered extrusion or a profile that is being - extruded along that axis and joined to other elements. - - Using an axis-based representation makes it easy for users and computers - to analyse connectivity and spatial relationships, as well as makes it - easy to parametrically edit these objects by simply stretching the start - or end of the axis. - - For now, only simple straight line axes are supported, represented by a - start and end coordinate. The order is important. For walls, the start - must be at the minimum local X ordinate, and the end at the maximum - local X ordinate. For beams and columns, the start is at the minimum - local Z ordinate, and the end of the maximum local Z ordinate. The first - coordinate is the "start" and the second coordinate is the "end". This - stat and end is then used to determine any parametric junctions with - other elements. - - Using an axis-representation is optional, but highly recommended for - "standard" representations of walls, beams, columns, and other - structural members. A rule of thumb is that if you can draw it as a line - on paper, you can probably represent it using an axis. - - :param context: The IfcGeometricRepresentationContext that the - representation is part of. This must be either a - Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D). - :type context: ifcopenshell.entity_instance - :param axis: The axis, as a list of two coordinates, the coordinates - being either a list of 2 or 3 float coordinates depending on whether - the axis is 2D or 3D. - :type axis: list[list[float]] - :return: The newly created IfcShapeRepresentation entity - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW") - axis = ifcopenshell.api.run("geometry.add_axis_representation", model, - context=context, axis=[(0.0, 0.0), (1.0, 0.0)]) - """ - self.file = file - self.settings = { - "context": context, - "axis": axis or [], - } - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) is_2d = len(self.settings["axis"][0]) == 2 @@ -82,9 +85,13 @@ class Usecase: curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) else: if is_2d: - curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False) + curve = self.file.createIfcIndexedPolyCurve( + self.file.createIfcCartesianPointList2D(points), None, False + ) else: - curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points), None, False) + curve = self.file.createIfcIndexedPolyCurve( + self.file.createIfcCartesianPointList3D(points), None, False + ) return self.file.createIfcShapeRepresentation( self.settings["context"], self.settings["context"].ContextIdentifier, diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py index 66a5bf3baf..3ec590a6f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py @@ -20,24 +20,27 @@ import ifcopenshell.util.unit import numpy as np -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "representation": None, - "operator": "DIFFERENCE", - # IfcHalfSpaceSolid, Mesh - "type": "IfcHalfSpaceSolid", - # The XY plane is the clipping boundary and +Z is removed. - "matrix": None, # A matrix to define a clipping Ifchalfspacesolid. - "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type - "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type - "should_force_faceted_brep": False, - "should_force_triangulation": False, - } - for key, value in settings.items(): - self.settings[key] = value +def add_boolean(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "representation": None, + "operator": "DIFFERENCE", + # IfcHalfSpaceSolid, Mesh + "type": "IfcHalfSpaceSolid", + # The XY plane is the clipping boundary and +Z is removed. + "matrix": None, # A matrix to define a clipping Ifchalfspacesolid. + "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type + "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type + "should_force_faceted_brep": False, + "should_force_triangulation": False, + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) if self.settings["type"] == "IfcHalfSpaceSolid": diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py index 4da8d5b1be..da7678aac4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py @@ -63,11 +63,7 @@ def create_ifc_door_lining( points = [p.xz for p in points] door_lining = builder.polyline(points, closed=True) - door_lining = builder.extrude( - door_lining, - size.y, - **builder.extrude_kwargs("Y") - ) + door_lining = builder.extrude(door_lining, size.y, **builder.extrude_kwargs("Y")) builder.translate(door_lining, position) return door_lining @@ -79,75 +75,78 @@ def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0, return box -class Usecase: - def __init__(self, file, **settings): - """units in settings expected to be in ifc project units""" - self.file = file - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - "overall_height": self.convert_si_to_unit(2.0), - "overall_width": self.convert_si_to_unit(0.9), - # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL, - # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT, - # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING, - # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT, - # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT, - # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL, - # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT, - # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT - "operation_type": "SINGLE_SWING_LEFT", # door type - "lining_properties": { - "LiningDepth": self.convert_si_to_unit(0.050), - "LiningThickness": self.convert_si_to_unit(0.050), - # offset from the outer side of the wall (by Y-axis) - "LiningOffset": self.convert_si_to_unit(0.0), - # offset from the wall - "LiningToPanelOffsetX": self.convert_si_to_unit(0.025), - # offset from the X-axis (unlike windows) - "LiningToPanelOffsetY": self.convert_si_to_unit(0.025), - # transom - vertical distance between door and window panels - "TransomThickness": self.convert_si_to_unit(0.000), - # TransomOffset - distance from the bottom door opening - # to the beginning of the transom - # unlike windows TransomOffset which goes to the center of the transom - "TransomOffset": self.convert_si_to_unit(1.525), - "ShapeAspectStyle": None, # DEPRECATED - # Casing cover wall faces around the opening - # on the left, right and upper sides - # Casing should be either on both sides of the wall or no casing - # If `LiningOffset` is present then therefore casing is not possible on outer wall - # therefore there will be no casing on inner wall either - "CasingDepth": self.convert_si_to_unit(0.005), - "CasingThickness": self.convert_si_to_unit(0.075), # by Z-axis - # Threshold covers the bottom side of the opening - "ThresholdDepth": self.convert_si_to_unit(0.1), - "ThresholdThickness": self.convert_si_to_unit(0.025), # by Z-axis - # offset by Y-axis - "ThresholdOffset": self.convert_si_to_unit(0.000), - }, - "panel_properties": { - "PanelDepth": self.convert_si_to_unit(0.035), # by Y - "PanelWidth": 1.0, # as ratio to the clear door opening - "FrameDepth": self.convert_si_to_unit(0.035), # by Y - "FrameThickness": self.convert_si_to_unit(0.035), # by X - # LEFT, MIDDLE, RIGHT, NOTDEFINED - "PanelPosition": ..., # NEVER USED - # defines the basic ways to describe how door panels operate - # basically how it opens - "PanelOperation": None, # NEVER USED - "ShapeAspectStyle": None, # DEPRECATED - }, - } - ) - for key, value in settings.items(): - self.settings[key] = value +def add_door_representation(file, **usecase_settings) -> None: + """units in usecase_settings expected to be in ifc project units""" + usecase = Usecase() + usecase.file = file + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + "overall_height": usecase.convert_si_to_unit(2.0), + "overall_width": usecase.convert_si_to_unit(0.9), + # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL, + # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT, + # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING, + # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT, + # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT, + # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL, + # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT, + # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT + "operation_type": "SINGLE_SWING_LEFT", # door type + "lining_properties": { + "LiningDepth": usecase.convert_si_to_unit(0.050), + "LiningThickness": usecase.convert_si_to_unit(0.050), + # offset from the outer side of the wall (by Y-axis) + "LiningOffset": usecase.convert_si_to_unit(0.0), + # offset from the wall + "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025), + # offset from the X-axis (unlike windows) + "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025), + # transom - vertical distance between door and window panels + "TransomThickness": usecase.convert_si_to_unit(0.000), + # TransomOffset - distance from the bottom door opening + # to the beginning of the transom + # unlike windows TransomOffset which goes to the center of the transom + "TransomOffset": usecase.convert_si_to_unit(1.525), + "ShapeAspectStyle": None, # DEPRECATED + # Casing cover wall faces around the opening + # on the left, right and upper sides + # Casing should be either on both sides of the wall or no casing + # If `LiningOffset` is present then therefore casing is not possible on outer wall + # therefore there will be no casing on inner wall either + "CasingDepth": usecase.convert_si_to_unit(0.005), + "CasingThickness": usecase.convert_si_to_unit(0.075), # by Z-axis + # Threshold covers the bottom side of the opening + "ThresholdDepth": usecase.convert_si_to_unit(0.1), + "ThresholdThickness": usecase.convert_si_to_unit(0.025), # by Z-axis + # offset by Y-axis + "ThresholdOffset": usecase.convert_si_to_unit(0.000), + }, + "panel_properties": { + "PanelDepth": usecase.convert_si_to_unit(0.035), # by Y + "PanelWidth": 1.0, # as ratio to the clear door opening + "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y + "FrameThickness": usecase.convert_si_to_unit(0.035), # by X + # LEFT, MIDDLE, RIGHT, NOTDEFINED + "PanelPosition": ..., # NEVER USED + # defines the basic ways to describe how door panels operate + # basically how it opens + "PanelOperation": None, # NEVER USED + "ShapeAspectStyle": None, # DEPRECATED + }, + } + ) + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): builder = ShapeBuilder(self.file) overall_height = self.settings["overall_height"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py index afdf95155a..976b48e5ce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py @@ -19,20 +19,17 @@ import ifcopenshell.util.unit -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "curves": [], # A list of IFC curves to include in the curve set - } - for key, value in settings.items(): - self.settings[key] = value +def add_footprint_representation(file, **usecase_settings) -> None: + settings = { + "context": None, # IfcGeometricRepresentationContext + "curves": [], # A list of IFC curves to include in the curve set + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - return self.file.createIfcShapeRepresentation( - self.settings["context"], - self.settings["context"].ContextIdentifier, - "GeometricCurveSet", - [self.file.createIfcGeometricCurveSet(self.settings["curves"])], - ) + return file.createIfcShapeRepresentation( + settings["context"], + settings["context"].ContextIdentifier, + "GeometricCurveSet", + [file.createIfcGeometricCurveSet(settings["curves"])], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py index ac2167a70e..fbe42063d9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py @@ -19,25 +19,28 @@ import ifcopenshell.util.unit -class Usecase: - def __init__(self, file: ifcopenshell.file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...] - # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...] - "vertices": None, # A list of coordinates - # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...] - "edges": None, # A list of edges, represented by vertex index pairs - # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...] - "faces": None, # A list of polygons, represented by vertex indices - "coordinate_offset": None, # Optionally apply a vector offset to all coordinates - "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different - "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets - } - for key, value in settings.items(): - self.settings[key] = value +def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...] + # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...] + "vertices": None, # A list of coordinates + # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...] + "edges": None, # A list of edges, represented by vertex index pairs + # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...] + "faces": None, # A list of polygons, represented by vertex indices + "coordinate_offset": None, # Optionally apply a vector offset to all coordinates + "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different + "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["unit_scale"] is None: self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py index c38aaf87df..025f09f0fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py @@ -21,22 +21,25 @@ import ifcopenshell.util.unit from ifcopenshell.util.data import Clipping -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "profile": None, - "depth": 1.0, - "cardinal_point": 5, - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - "placement_zx_axes": (None, None), - } - for key, value in settings.items(): - self.settings[key] = value +def add_profile_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "profile": None, + "depth": 1.0, + "cardinal_point": 5, + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + "placement_zx_axes": (None, None), + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py index b597633678..9de884c8c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py @@ -31,39 +31,42 @@ def mm(x): return x / 1000 +def add_railing_representation(file, **usecase_settings) -> None: + """ + units in usecase_settings expected to be in ifc project units + + `railing_path` is a list of point coordinates for the railing path, + coordinates are expected to be at the top of the railing, not at the center + + `railing_path` is expected to be a list of Vector objects + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + "railing_type": "WALL_MOUNTED_HANDRAIL", + "railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]), + "use_manual_supports": False, + "support_spacing": usecase.convert_si_to_unit(mm(1000)), + "railing_diameter": usecase.convert_si_to_unit(mm(50)), + "clear_width": usecase.convert_si_to_unit(mm(40)), + "terminal_type": "180", + "height": usecase.convert_si_to_unit(mm(1000)), + "looped_path": False, + } + ) + + for key, value in usecase_settings.items(): + usecase.settings[key] = value + + if usecase.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL": + raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.') + return usecase.execute() + + class Usecase: - def __init__(self, file, **settings): - """ - units in settings expected to be in ifc project units - - `railing_path` is a list of point coordinates for the railing path, - coordinates are expected to be at the top of the railing, not at the center - - `railing_path` is expected to be a list of Vector objects - """ - self.file = file - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - "railing_type": "WALL_MOUNTED_HANDRAIL", - "railing_path": self.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]), - "use_manual_supports": False, - "support_spacing": self.convert_si_to_unit(mm(1000)), - "railing_diameter": self.convert_si_to_unit(mm(50)), - "clear_width": self.convert_si_to_unit(mm(40)), - "terminal_type": "180", - "height": self.convert_si_to_unit(mm(1000)), - "looped_path": False, - } - ) - - for key, value in settings.items(): - self.settings[key] = value - - if self.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL": - raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.') - def execute(self): arc_points = [] items_3d = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 155bfb2bea..6c9a8da058 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -28,37 +28,40 @@ X_AXIS = Vector((1, 0, 0)) EPSILON = 1e-6 -class Usecase: - def __init__(self, file: ifcopenshell.file, **settings): - # TODO: This usecase currently depends on Blender's data model - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now - "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now - "coordinate_offset": None, # Optionally apply a vector offset to all coordinates - "total_items": 1, # How many representation items to create - "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different - "should_force_faceted_brep": False, # If we should force faceted breps for meshes - "should_force_triangulation": False, # If we should force triangulation for meshes - "should_generate_uvs": False, # If UV coordinates should also be generated - # Possible IFC representation classes: - # IfcExtrudedAreaSolid/IfcRectangleProfileDef - # IfcExtrudedAreaSolid/IfcCircleProfileDef - # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef - # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids - # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage - # IfcGeometricCurveSet/IfcTextLiteral - # IfcTextLiteral - "ifc_representation_class": None, # Whether to cast a mesh into a particular class - "profile_set_usage": None, # The material profile set if the extrusion requires it - "text_literal": None, # The text literal if the representation requires it - } - self.ifc_vertices = [] - for key, value in settings.items(): - self.settings[key] = value +def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance: + usecase = Usecase() + # TODO: This usecase currently depends on Blender's data model + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now + "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now + "coordinate_offset": None, # Optionally apply a vector offset to all coordinates + "total_items": 1, # How many representation items to create + "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different + "should_force_faceted_brep": False, # If we should force faceted breps for meshes + "should_force_triangulation": False, # If we should force triangulation for meshes + "should_generate_uvs": False, # If UV coordinates should also be generated + # Possible IFC representation classes: + # IfcExtrudedAreaSolid/IfcRectangleProfileDef + # IfcExtrudedAreaSolid/IfcCircleProfileDef + # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef + # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids + # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage + # IfcGeometricCurveSet/IfcTextLiteral + # IfcTextLiteral + "ifc_representation_class": None, # Whether to cast a mesh into a particular class + "profile_set_usage": None, # The material profile set if the extrusion requires it + "text_literal": None, # The text literal if the representation requires it + } + usecase.ifc_vertices = [] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() - def execute(self) -> ifcopenshell.entity_instance: + +class Usecase: + def execute(self): self.is_manifold = None if ( isinstance(self.settings["geometry"], bpy.types.Mesh) @@ -374,10 +377,12 @@ class Usecase: return items def create_plane(self, polygon): - return self.file.createIfcPlane(Position=self.file.createIfcAxis2Placement3D( - Location=self.file.createIfcCartesianPoint(polygon.center), - Axis=self.file.createIfcDirection(polygon.normal), - )) + return self.file.createIfcPlane( + Position=self.file.createIfcAxis2Placement3D( + Location=self.file.createIfcCartesianPoint(polygon.center), + Axis=self.file.createIfcDirection(polygon.normal), + ) + ) def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]: items = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py index 2edd68d5c1..7514ade38f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py @@ -20,20 +20,23 @@ import ifcopenshell.util.unit from math import sin, cos -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "depth": 0.2, - "x_angle": 0, # Radians - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - } - for key, value in settings.items(): - self.settings[key] = value +def add_slab_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "depth": 0.2, + "x_angle": 0, # Radians + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) return self.file.createIfcShapeRepresentation( diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py index 6aa5466f58..504a89078b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py @@ -21,25 +21,28 @@ from math import sin, cos from ifcopenshell.util.data import Clipping -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "length": 1.0, - "height": 3.0, - "offset": 0.0, - "thickness": 0.2, - # Sloped walls along the wall's X axis, provided in radians - "x_angle": 0, - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - "booleans": [], # Any existing IfcBooleanResults - } - for key, value in settings.items(): - self.settings[key] = value +def add_wall_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "length": 1.0, + "height": 3.0, + "offset": 0.0, + "thickness": 0.2, + # Sloped walls along the wall's X axis, provided in radians + "x_angle": 0, + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + "booleans": [], # Any existing IfcBooleanResults + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py index 94b97636aa..b0a46e69ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py @@ -56,12 +56,7 @@ def create_ifc_window_frame_simple( th_left, th_up, th_right, th_bottom = thickness def get_extruded_profile(profile): - return builder.extrude( - profile, - size.y, - position=position, - **builder.extrude_kwargs("Y") - ) + return builder.extrude(profile, size.y, position=position, **builder.extrude_kwargs("Y")) # if all lining sides are present then we can just use two rectangles # as inner and outer curves of the profile @@ -207,12 +202,7 @@ def create_ifc_window( glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0) glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0]) - glass = builder.extrude( - glass_rect, - glass_thickness, - position=glass_position, - **builder.extrude_kwargs("Y") - ) + glass = builder.extrude(glass_rect, glass_thickness, position=glass_position, **builder.extrude_kwargs("Y")) output_items = [lining_items, frame_extruded_items, [glass]] builder.translate(chain(*output_items), position) @@ -220,73 +210,76 @@ def create_ifc_window( return output_items -class Usecase: - def __init__(self, file, **settings): - """units in settings expected to be in ifc project units""" - self.file = file - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, - # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, - # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL - "partition_type": "SINGLE_PANEL", - "overall_height": self.convert_si_to_unit(0.9), - "overall_width": self.convert_si_to_unit(0.6), - "lining_properties": { - "LiningDepth": self.convert_si_to_unit(0.050), - "LiningThickness": self.convert_si_to_unit(0.050), - "LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall - # offset from the wall - "LiningToPanelOffsetX": self.convert_si_to_unit(0.025), - # offset from the lining - # that way it allows you to define overall_depth constant between all panels - # and still have panels with different size: - # overall_depth = lining_depth + offset_y - # full offset from X axis = overall_depth - frame_depth - "LiningToPanelOffsetY": self.convert_si_to_unit(0.025), - # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, - # TriplePanelLeft, TriplePanelRight - # mullion - horizontal distance between panels - "MullionThickness": self.convert_si_to_unit(0.050), - # distance from the first lining to the mullion center - "FirstMullionOffset": self.convert_si_to_unit(0.3), - # applies to TriplePanelVertical - # distance from the first lining to the second mullion center - "SecondMullionOffset": self.convert_si_to_unit(0.45), - # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, - # TriplePanelLeft, TriplePanelRight - # works similar way to mullion - "TransomThickness": self.convert_si_to_unit(0.050), - "FirstTransomOffset": self.convert_si_to_unit(0.3), - # applies to TriplePanelHorizontal - "SecondTransomOffset": self.convert_si_to_unit(0.6), +def add_window_representation(file, **usecase_settings) -> None: + """units in usecase_settings expected to be in ifc project units""" + usecase = Usecase() + usecase.file = file + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, + # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, + # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL + "partition_type": "SINGLE_PANEL", + "overall_height": usecase.convert_si_to_unit(0.9), + "overall_width": usecase.convert_si_to_unit(0.6), + "lining_properties": { + "LiningDepth": usecase.convert_si_to_unit(0.050), + "LiningThickness": usecase.convert_si_to_unit(0.050), + "LiningOffset": usecase.convert_si_to_unit(0.050), # offset to the wall + # offset from the wall + "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025), + # offset from the lining + # that way it allows you to define overall_depth constant between all panels + # and still have panels with different size: + # overall_depth = lining_depth + offset_y + # full offset from X axis = overall_depth - frame_depth + "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025), + # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, + # TriplePanelLeft, TriplePanelRight + # mullion - horizontal distance between panels + "MullionThickness": usecase.convert_si_to_unit(0.050), + # distance from the first lining to the mullion center + "FirstMullionOffset": usecase.convert_si_to_unit(0.3), + # applies to TriplePanelVertical + # distance from the first lining to the second mullion center + "SecondMullionOffset": usecase.convert_si_to_unit(0.45), + # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, + # TriplePanelLeft, TriplePanelRight + # works similar way to mullion + "TransomThickness": usecase.convert_si_to_unit(0.050), + "FirstTransomOffset": usecase.convert_si_to_unit(0.3), + # applies to TriplePanelHorizontal + "SecondTransomOffset": usecase.convert_si_to_unit(0.6), + "ShapeAspectStyle": None, # DEPRECATED + }, + "panel_properties": [ + { + "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y + "FrameThickness": usecase.convert_si_to_unit(0.035), # by X + # BOTTOM, LEFT, MIDDLE, RIGHT, TOP + "PanelPosition": ..., # NEVER USED + # defines the basic ways to describe how window panels operate + # how it's hanged, how it opens + "OperationType": None, # NEVER USED "ShapeAspectStyle": None, # DEPRECATED }, - "panel_properties": [ - { - "FrameDepth": self.convert_si_to_unit(0.035), # by Y - "FrameThickness": self.convert_si_to_unit(0.035), # by X - # BOTTOM, LEFT, MIDDLE, RIGHT, TOP - "PanelPosition": ..., # NEVER USED - # defines the basic ways to describe how window panels operate - # how it's hanged, how it opens - "OperationType": None, # NEVER USED - "ShapeAspectStyle": None, # DEPRECATED - }, - ], - } - ) + ], + } + ) - for key, value in settings.items(): - self.settings[key] = value - self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]] + return usecase.execute() + +class Usecase: def execute(self): builder = ShapeBuilder(self.file) overall_height = self.settings["overall_height"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py index 41d7f9422b..1df964d845 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py @@ -20,13 +20,16 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"product": None, "representation": None} - for key, value in settings.items(): - self.settings[key] = value +def assign_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": None, "representation": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["product"].is_a("IfcProduct"): product_type = ifcopenshell.util.element.get_type(self.settings["product"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py index 9339752d4c..64df2e59d6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py @@ -21,44 +21,41 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "description": None, - } - for key, value in settings.items(): - self.settings[key] = value +def connect_element(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "description": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] + incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - for rel in self.settings["relating_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: - rel.Description = self.settings["description"] - return rel + for rel in settings["relating_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]: + rel.Description = settings["description"] + return rel - return self.file.createIfcRelConnectsElements( - ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - Description=self.settings["description"], - RelatingElement=self.settings["relating_element"], - RelatedElement=self.settings["related_element"], - ) + return file.createIfcRelConnectsElements( + ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + Description=settings["description"], + RelatingElement=settings["relating_element"], + RelatedElement=settings["related_element"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py index 1a610a7d2c..7cc60c4ef1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py @@ -21,76 +21,73 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "relating_connection": "NOTDEFINED", - "related_connection": "NOTDEFINED", - "description": None, - } - for key, value in settings.items(): - self.settings[key] = value +def connect_path(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "relating_connection": "NOTDEFINED", + "related_connection": "NOTDEFINED", + "description": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedTo: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if rel.RelatedElement == self.settings["related_element"]: - incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == self.settings["relating_connection"] - ): - incompatible_connections.append(rel) + incompatible_connections = [] + for rel in settings["relating_element"].ConnectedTo: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if rel.RelatedElement == settings["related_element"]: + incompatible_connections.append(rel) + elif ( + rel.RelatingConnectionType in ["ATSTART", "ATEND"] + and rel.RelatingConnectionType == settings["relating_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["relating_element"].ConnectedFrom: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == self.settings["relating_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if ( + rel.RelatedConnectionType in ["ATSTART", "ATEND"] + and rel.RelatedConnectionType == settings["relating_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedFrom: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == self.settings["related_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedFrom: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if ( + rel.RelatedConnectionType in ["ATSTART", "ATEND"] + and rel.RelatedConnectionType == settings["related_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == self.settings["related_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) + elif ( + rel.RelatingConnectionType in ["ATSTART", "ATEND"] + and rel.RelatingConnectionType == settings["related_connection"] + ): + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - return self.file.createIfcRelConnectsPathElements( - ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - Description=self.settings["description"], - RelatingElement=self.settings["relating_element"], - RelatedElement=self.settings["related_element"], - RelatingConnectionType=self.settings["relating_connection"], - RelatedConnectionType=self.settings["related_connection"], - RelatingPriorities=[], - RelatedPriorities=[], - ) + return file.createIfcRelConnectsPathElements( + ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + Description=settings["description"], + RelatingElement=settings["relating_element"], + RelatedElement=settings["related_element"], + RelatingConnectionType=settings["relating_connection"], + RelatedConnectionType=settings["related_connection"], + RelatingPriorities=[], + RelatedPriorities=[], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py index e228730974..cd508d4a3e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py @@ -21,20 +21,25 @@ import ifcopenshell.api import ifcopenshell.util.unit -class Usecase: - def __init__(self, file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True): - self.file = file - self.settings = { - "element": element, - "context": context, - "p1": p1, - "p2": p2, - "elevation": elevation, - "height": height, - "thickness": thickness, - "is_si": is_si - } +def create_2pt_wall( + file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True +) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "context": context, + "p1": p1, + "p2": p2, + "elevation": elevation, + "height": height, + "thickness": thickness, + "is_si": is_si, + } + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) @@ -44,9 +49,9 @@ class Usecase: length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"])) if not self.settings["is_si"]: - length=self.convert_unit_to_si(length) - self.settings["height"]=self.convert_unit_to_si(self.settings["height"]) - self.settings["thickness"]=self.convert_unit_to_si(self.settings["thickness"]) + length = self.convert_unit_to_si(length) + self.settings["height"] = self.convert_unit_to_si(self.settings["height"]) + self.settings["thickness"] = self.convert_unit_to_si(self.settings["thickness"]) self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0]) self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1]) self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py index 680b3e85e7..1e1eaaa82b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py @@ -20,38 +20,35 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - } - for key, value in settings.items(): - self.settings[key] = value +def disconnect_element(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] + incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["relating_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]: + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py index 8e52c7e4ff..14bbaf9e7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py @@ -21,38 +21,35 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "element": None, - "connection_type": None, - } - for key, value in settings.items(): - self.settings[key] = value +def disconnect_path(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "element": None, + "connection_type": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - if self.settings["connection_type"] and self.settings["element"]: - connections = [ - r - for r in self.settings["element"].ConnectedTo - if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == self.settings["connection_type"] - ] + [ - r - for r in self.settings["element"].ConnectedFrom - if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == self.settings["connection_type"] - ] - else: - connections = [ - r - for r in self.settings["relating_element"].ConnectedTo - if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == self.settings["related_element"] - ] + if settings["connection_type"] and settings["element"]: + connections = [ + r + for r in settings["element"].ConnectedTo + if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"] + ] + [ + r + for r in settings["element"].ConnectedFrom + if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"] + ] + else: + connections = [ + r + for r in settings["relating_element"].ConnectedTo + if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"] + ] - for connection in set(connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for connection in set(connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py index 2f442388b6..768468e03a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py @@ -27,24 +27,26 @@ from typing import Optional, Union NPArrayOfFloats = npt.NDArray[np.float64] -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - product: ifcopenshell.entity_instance, - matrix: Optional[NPArrayOfFloats] = None, - is_si=True, - should_transform_children=False, - ): - self.file = file - self.settings = { - "product": product, - "matrix": matrix if matrix is not None else np.eye(4), - "is_si": is_si, - "should_transform_children": should_transform_children, - } +def edit_object_placement( + file: ifcopenshell.file, + product: ifcopenshell.entity_instance, + matrix: Optional[NPArrayOfFloats] = None, + is_si=True, + should_transform_children=False, +) -> ifcopenshell.entity_instance: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "product": product, + "matrix": matrix if matrix is not None else np.eye(4), + "is_si": is_si, + "should_transform_children": should_transform_children, + } + return usecase.execute() - def execute(self) -> ifcopenshell.entity_instance: + +class Usecase: + def execute(self): if not hasattr(self.settings["product"], "ObjectPlacement"): return self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py index 1e4b4e2207..83e1e1e821 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py @@ -17,14 +17,17 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"representation": None} - self.ifc_vertices = [] - for key, value in settings.items(): - self.settings[key] = value +def map_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"representation": None} + usecase.ifc_vertices = [] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): mapping_source = self.get_mapping_source() diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py index 85ca854233..5d81203a5d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py @@ -19,13 +19,16 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"item": None} - for key, value in settings.items(): - self.settings[key] = value +def remove_boolean(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"item": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): item = None for inverse in self.file.get_inverse(self.settings["item"]): diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py index d7893ea317..7aafb751e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py @@ -19,62 +19,57 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, representation: ifcopenshell.entity_instance): - """Remove a representation. +def remove_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None: + """Remove a representation. - Also purges representation items and their related elements - like IfcStyledItem, tessellated facesets colours and UV map. + Also purges representation items and their related elements + like IfcStyledItem, tessellated facesets colours and UV map. - :param representation: IfcRepresentation to remove. - Note that it's expected that IfcRepresentation won't be in use - before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect) - otherwise representation won't be removed. - :type representation: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"representation": representation} + :param representation: IfcRepresentation to remove. + Note that it's expected that IfcRepresentation won't be in use + before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect) + otherwise representation won't be removed. + :type representation: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"representation": representation} - def execute(self) -> None: - styled_items = set() - presentation_layer_assignments = set() - textures = set() - colours = set() - for subelement in self.file.traverse(self.settings["representation"]): - if subelement.is_a("IfcRepresentationItem"): - [styled_items.add(s) for s in subelement.StyledByItem or []] - # IFC2X3 is using LayerAssignments - for s in ( - subelement.LayerAssignment - if hasattr(subelement, "LayerAssignment") - else subelement.LayerAssignments - ): - presentation_layer_assignments.add(s) - # IfcTessellatedFaceSet inverses - [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []] - [colours.add(t) for t in getattr(subelement, "HasColours", []) or []] - elif subelement.is_a("IfcRepresentation"): - for layer in subelement.LayerAssignments: - presentation_layer_assignments.add(layer) + styled_items = set() + presentation_layer_assignments = set() + textures = set() + colours = set() + for subelement in file.traverse(settings["representation"]): + if subelement.is_a("IfcRepresentationItem"): + [styled_items.add(s) for s in subelement.StyledByItem or []] + # IFC2X3 is using LayerAssignments + for s in ( + subelement.LayerAssignment if hasattr(subelement, "LayerAssignment") else subelement.LayerAssignments + ): + presentation_layer_assignments.add(s) + # IfcTessellatedFaceSet inverses + [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []] + [colours.add(t) for t in getattr(subelement, "HasColours", []) or []] + elif subelement.is_a("IfcRepresentation"): + for layer in subelement.LayerAssignments: + presentation_layer_assignments.add(layer) - ifcopenshell.util.element.remove_deep2( - self.file, - self.settings["representation"], - also_consider=list(styled_items | presentation_layer_assignments | colours), - do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"), - ) + ifcopenshell.util.element.remove_deep2( + file, + settings["representation"], + also_consider=list(styled_items | presentation_layer_assignments | colours), + do_not_delete=file.by_type("IfcGeometricRepresentationContext"), + ) - for texture in textures: - ifcopenshell.util.element.remove_deep2(self.file, texture) - for colour in colours: - ifcopenshell.util.element.remove_deep2(self.file, colour) + for texture in textures: + ifcopenshell.util.element.remove_deep2(file, texture) + for colour in colours: + ifcopenshell.util.element.remove_deep2(file, colour) - to_delete = getattr(self.file, "to_delete", ()) - for element in styled_items: - if not element.Item or element.Item in to_delete: - self.file.remove(element) - for element in presentation_layer_assignments: - if all(item in to_delete for item in element.AssignedItems): - self.file.remove(element) + to_delete = getattr(file, "to_delete", ()) + for element in styled_items: + if not element.Item or element.Item in to_delete: + file.remove(element) + for element in presentation_layer_assignments: + if all(item in to_delete for item in element.AssignedItems): + file.remove(element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py index 389aad88d6..83b1570ac6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py @@ -20,13 +20,16 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"product": None, "representation": None} - for key, value in settings.items(): - self.settings[key] = value +def unassign_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": None, "representation": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["product"].is_a("IfcProduct"): self.unassign_product_representation(self.settings["product"], self.settings["representation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py index e0caddbe3c..1aa858db17 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_georeferencing import add_georeferencing +from .edit_georeferencing import edit_georeferencing +from .remove_georeferencing import remove_georeferencing diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py index a957970a91..5da8819e47 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file): - """Add empty georeferencing entities to a model +def add_georeferencing(file) -> None: + """Add empty georeferencing entities to a model - By default, models are not georeferenced. Georeferencing requires two - entities: a definition of the projected coordinated reference system - (CRS) used, and the transformation parameters between any local coordinate - system and that projected CRS if any. + By default, models are not georeferenced. Georeferencing requires two + entities: a definition of the projected coordinated reference system + (CRS) used, and the transformation parameters between any local coordinate + system and that projected CRS if any. - This function will create the entities to store the projected CRS and - map conversion transformation, but will leave all the parameters blank. - It is this the users responsibility to specify the correct - georeferencing parameters. See - ifcopenshell.api.georeference.edit_georeferencing. + This function will create the entities to store the projected CRS and + map conversion transformation, but will leave all the parameters blank. + It is this the users responsibility to specify the correct + georeferencing parameters. See + ifcopenshell.api.georeference.edit_georeferencing. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("georeference.add_georeferencing", model) - """ - self.file = file + ifcopenshell.api.run("georeference.add_georeferencing", model) + """ - def execute(self): - source_crs = None - for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): - if context.ContextType == "Model": - source_crs = context - break - if not source_crs: - return - projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""}) - self.file.create_entity( - "IfcMapConversion", - **{ - "SourceCRS": source_crs, - "TargetCRS": projected_crs, - "Eastings": 0, - "Northings": 0, - "OrthogonalHeight": 0, - } - ) + source_crs = None + for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): + if context.ContextType == "Model": + source_crs = context + break + if not source_crs: + return + projected_crs = file.create_entity("IfcProjectedCRS", **{"Name": ""}) + file.create_entity( + "IfcMapConversion", + **{ + "SourceCRS": source_crs, + "TargetCRS": projected_crs, + "Eastings": 0, + "Northings": 0, + "OrthogonalHeight": 0, + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py index 77554e1f7f..f4118d96e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py @@ -17,77 +17,80 @@ # along with IfcOpenShell. If not, see . +def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_north=None) -> None: + """Edits the attributes of a map conversion, projected CRS, and true north + + Setting the correct georeferencing parameters is a complex topic and + should ideally be done with three parties present: the lead architect, + surveyor, and a third-party digital engineer with expertise in IFC to + moderate. For more information, read the BlenderBIM Add-on documentation + for Georeferencing: + https://docs.blenderbim.org/users/georeferencing.html + + For more information about the attributes and data types of an + IfcMapConversion, consult the IFC documentation. + + For more information about the attributes and data types of an + IfcProjectedCRS, consult the IFC documentation. + + True north is defined as a unitised 2D vector pointing to true north. + Note that true north is not part of georeferencing, and is only + optionally provided as a reference value, typically for solar analysis. + + See ifcopenshell.util.geolocation for more utilities to convert to and + from local and map coordinates to check your results. + + :param map_conversion: The IfcMapConversion dictionary of attribute + names and values you want to edit. + :type map_conversion: dict, optional + :param projected_crs: The IfcProjectedCRS dictionary of attribute + names and values you want to edit. + :type projected_crs: dict, optional + :param true_north: A unitised 2D vector, where each ordinate is a float + :type true_north: list[float] + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("georeference.add_georeferencing", model) + # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone + # 56, typically used in Sydney, Australia) but with no local + # coordinates. This is only recommended for horizontal construction + # projects, not for vertical construction (such as buildings). + ifcopenshell.api.run("georeference.edit_georeferencing", model, + projected_crs={"Name": "EPSG:7856"}) + + # For buildings, it is almost always recommended to specify map + # conversion parameters to a false origin and orientation to project + # north. See the diagram in the BlenderBIM Add-on Georeferencing + # documentation for correct calculation of the X Axis Abcissa and + # Ordinate. + ifcopenshell.api.run("georeference.edit_georeferencing", model, + projected_crs={"Name": "EPSG:7856"}, + map_conversion={ + "Eastings": 335087.17, # The architect nominates a false origin + "Northings": 6251635.41, # The architect nominates a false origin + # Note: this is the angle difference between Project North + # and Grid North. Remember: True North should never be used! + "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north + "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north + "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor! + }) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "map_conversion": map_conversion or {}, + "projected_crs": projected_crs or {}, + "true_north": true_north or [], + } + return usecase.execute() + + class Usecase: - def __init__(self, file, map_conversion=None, projected_crs=None, true_north=None): - """Edits the attributes of a map conversion, projected CRS, and true north - - Setting the correct georeferencing parameters is a complex topic and - should ideally be done with three parties present: the lead architect, - surveyor, and a third-party digital engineer with expertise in IFC to - moderate. For more information, read the BlenderBIM Add-on documentation - for Georeferencing: - https://docs.blenderbim.org/users/georeferencing.html - - For more information about the attributes and data types of an - IfcMapConversion, consult the IFC documentation. - - For more information about the attributes and data types of an - IfcProjectedCRS, consult the IFC documentation. - - True north is defined as a unitised 2D vector pointing to true north. - Note that true north is not part of georeferencing, and is only - optionally provided as a reference value, typically for solar analysis. - - See ifcopenshell.util.geolocation for more utilities to convert to and - from local and map coordinates to check your results. - - :param map_conversion: The IfcMapConversion dictionary of attribute - names and values you want to edit. - :type map_conversion: dict, optional - :param projected_crs: The IfcProjectedCRS dictionary of attribute - names and values you want to edit. - :type projected_crs: dict, optional - :param true_north: A unitised 2D vector, where each ordinate is a float - :type true_north: list[float] - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("georeference.add_georeferencing", model) - # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone - # 56, typically used in Sydney, Australia) but with no local - # coordinates. This is only recommended for horizontal construction - # projects, not for vertical construction (such as buildings). - ifcopenshell.api.run("georeference.edit_georeferencing", model, - projected_crs={"Name": "EPSG:7856"}) - - # For buildings, it is almost always recommended to specify map - # conversion parameters to a false origin and orientation to project - # north. See the diagram in the BlenderBIM Add-on Georeferencing - # documentation for correct calculation of the X Axis Abcissa and - # Ordinate. - ifcopenshell.api.run("georeference.edit_georeferencing", model, - projected_crs={"Name": "EPSG:7856"}, - map_conversion={ - "Eastings": 335087.17, # The architect nominates a false origin - "Northings": 6251635.41, # The architect nominates a false origin - # Note: this is the angle difference between Project North - # and Grid North. Remember: True North should never be used! - "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north - "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north - "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor! - }) - """ - self.file = file - self.settings = { - "map_conversion": map_conversion or {}, - "projected_crs": projected_crs or {}, - "true_north": true_north or [], - } - def execute(self): map_conversion = self.file.by_type("IfcMapConversion")[0] projected_crs = self.file.by_type("IfcProjectedCRS")[0] diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py index 2ac32a0c6e..3d3941ed7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py @@ -17,29 +17,26 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file): - """Remove georeferencing data +def remove_georeferencing(file) -> None: + """Remove georeferencing data - All georeferencing parameters such as projected CRS and map conversion - data will be lost. + All georeferencing parameters such as projected CRS and map conversion + data will be lost. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - ifcopenshell.api.run("georeference.add_georeferencing", model) - # Let's change our mind - ifcopenshell.api.run("georeference.remove_georeferencing", model) - """ - self.file = file + ifcopenshell.api.run("georeference.add_georeferencing", model) + # Let's change our mind + ifcopenshell.api.run("georeference.remove_georeferencing", model) + """ - def execute(self): - map_conversion = self.file.by_type("IfcMapConversion")[0] - projected_crs = self.file.by_type("IfcProjectedCRS")[0] - if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1: - # TODO: go deeper for conversion units - self.file.remove(projected_crs.MapUnit) - self.file.remove(projected_crs) - self.file.remove(map_conversion) + map_conversion = file.by_type("IfcMapConversion")[0] + projected_crs = file.by_type("IfcProjectedCRS")[0] + if projected_crs.MapUnit and len(file.get_inverse(projected_crs.MapUnit)) == 1: + # TODO: go deeper for conversion units + file.remove(projected_crs.MapUnit) + file.remove(projected_crs) + file.remove(map_conversion) diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py index e0caddbe3c..c66e86a668 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .create_axis_curve import create_axis_curve +from .create_grid_axis import create_grid_axis +from .remove_grid_axis import remove_grid_axis diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py index 21fead74e5..2f3520c662 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -22,46 +22,49 @@ import ifcopenshell.util.placement from mathutils import Matrix # For now, we depend on Blender +def create_axis_curve(file, axis_curve=None, grid_axis=None) -> None: + """Adds curve geometry to a grid axis to represent the axis extents + + This currently depends on the Blender geometry kernel to function. + + An IFC grid will have a minimum of two axes (typically perpendicular). Each + axis will then have a line which represents the extents of the axis. + + :param axis_curve: The Blender object that contains a mesh data block with a + single edge. + :type axis_curve: bpy.types.Object + :param grid_axis: The IfcGridAxis element to add geometry to. + :type grid_axis: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) + + # Assume you have these Blender objects in your active Blender session + obj1 = bpy.data.objects.get("AxisA") + obj2 = bpy.data.objects.get("Axis1") + ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a) + ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "axis_curve": axis_curve, # A Blender object + "grid_axis": grid_axis, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, axis_curve=None, grid_axis=None): - """Adds curve geometry to a grid axis to represent the axis extents - - This currently depends on the Blender geometry kernel to function. - - An IFC grid will have a minimum of two axes (typically perpendicular). Each - axis will then have a line which represents the extents of the axis. - - :param axis_curve: The Blender object that contains a mesh data block with a - single edge. - :type axis_curve: bpy.types.Object - :param grid_axis: The IfcGridAxis element to add geometry to. - :type grid_axis: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) - - # Assume you have these Blender objects in your active Blender session - obj1 = bpy.data.objects.get("AxisA") - obj2 = bpy.data.objects.get("Axis1") - ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a) - ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1) - """ - self.file = file - self.settings = { - "axis_curve": axis_curve, # A Blender object - "grid_axis": grid_axis, - } - def execute(self): existing_curve = self.settings["grid_axis"].AxisCurve if existing_curve and len(self.file.get_inverse(existing_curve)) == 1: diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py index de667bb5bc..f089b43b98 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py @@ -17,69 +17,66 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None): - """Adds a new grid axis to a grid +def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None) -> None: + """Adds a new grid axis to a grid - An IFC grid will typically have a minimum of two axes which will be - perpendicular to one another. Grids may be rectangular (typically - perpendicular lines), radial (where one set of axes is a circle and the - other is a line), or triangular (three sets of axes, each at a different - angle to one another). + An IFC grid will typically have a minimum of two axes which will be + perpendicular to one another. Grids may be rectangular (typically + perpendicular lines), radial (where one set of axes is a circle and the + other is a line), or triangular (three sets of axes, each at a different + angle to one another). - For a simple rectangular grid, the "UAxes" are a set of one or more - horizontal axes, which are typically labeled with the convention of A, - B, C, etc. The "VAxes" is another set of one or more vertical axes, - typically labeled with the convention of 1, 2, 3, etc. These axes are - horizontal or vertical relative to project north. + For a simple rectangular grid, the "UAxes" are a set of one or more + horizontal axes, which are typically labeled with the convention of A, + B, C, etc. The "VAxes" is another set of one or more vertical axes, + typically labeled with the convention of 1, 2, 3, etc. These axes are + horizontal or vertical relative to project north. - For a radial grid, the "UAxes" are straight lines, typically radiating - from a central point. The "VAxes" are circular perimeters, with the - center of these circles being the same central point. + For a radial grid, the "UAxes" are straight lines, typically radiating + from a central point. The "VAxes" are circular perimeters, with the + center of these circles being the same central point. - For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one - or more straight lines. + For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one + or more straight lines. - :param axis_tag: The name of the axis, that would typically be labeled - on drawings or described on site during coordination, such as A, B, - C, 1, 2, 3, etc. Defaults to "A". - :type axis_tag: str, optional - :param same_sense: Determines whether the direction of the axis's line - is reversed. True means the direction the geometry is defined in - represents the direction of the axis. False means the direction is - reversed. Leave as True if unsure. Defaults to "True". - :type same_sense: bool, optional - :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on - which set of axes the new axis you are adding should belong to. - Defaults to "UAxes". - :type uvw_axes: str, optional - :param grid: The IfcGrid you are adding the axis to. - :type grid: ifcopenshell.entity_instance - :return: The newly created IfcGridAxis - :rtype: ifcopenshell.entity_instance + :param axis_tag: The name of the axis, that would typically be labeled + on drawings or described on site during coordination, such as A, B, + C, 1, 2, 3, etc. Defaults to "A". + :type axis_tag: str, optional + :param same_sense: Determines whether the direction of the axis's line + is reversed. True means the direction the geometry is defined in + represents the direction of the axis. False means the direction is + reversed. Leave as True if unsure. Defaults to "True". + :type same_sense: bool, optional + :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on + which set of axes the new axis you are adding should belong to. + Defaults to "UAxes". + :type uvw_axes: str, optional + :param grid: The IfcGrid you are adding the axis to. + :type grid: ifcopenshell.entity_instance + :return: The newly created IfcGridAxis + :rtype: ifcopenshell.entity_instance - Example: + Example: - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) - """ - self.file = file - self.settings = { - "axis_tag": axis_tag or "A", - "same_sense": same_sense or True, - "uvw_axes": uvw_axes or "UAxes", # Choose which axes - "grid": grid, - } + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) + """ + settings = { + "axis_tag": axis_tag or "A", + "same_sense": same_sense or True, + "uvw_axes": uvw_axes or "UAxes", # Choose which axes + "grid": grid, + } - def execute(self): - element = self.file.create_entity( - "IfcGridAxis", **{"AxisTag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]} - ) - axes = list(getattr(self.settings["grid"], self.settings["uvw_axes"]) or []) - axes.append(element) - setattr(self.settings["grid"], self.settings["uvw_axes"], axes) - return element + element = file.create_entity( + "IfcGridAxis", **{"AxisTag": settings["axis_tag"], "SameSense": settings["same_sense"]} + ) + axes = list(getattr(settings["grid"], settings["uvw_axes"]) or []) + axes.append(element) + setattr(settings["grid"], settings["uvw_axes"], axes) + return element diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py index b380778a67..51032ed52f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py @@ -19,36 +19,33 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, axis=None): - """Removes a grid axis from a grid +def remove_grid_axis(file, axis=None) -> None: + """Removes a grid axis from a grid - :param axis: The IfcGridAxis you want to remove. - :type axis: ifcopenshell.entity_instance - :return: None - :rtype: None + :param axis: The IfcGridAxis you want to remove. + :type axis: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) - # Let's create a third so we can remove it later - axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="2", uvw_axes="VAxes", grid=grid) + # Let's create a third so we can remove it later + axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="2", uvw_axes="VAxes", grid=grid) - # Let's remove it! - ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2) - """ - self.file = file - self.settings = {"axis": axis} + # Let's remove it! + ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2) + """ + settings = {"axis": axis} - def execute(self): - if len(self.file.get_inverse(self.settings["axis"].AxisCurve)) == 1: - ifcopenshell.util.element.remove_deep(self.file, self.settings["axis"].AxisCurve) - self.file.remove(self.settings["axis"].AxisCurve) - self.file.remove(self.settings["axis"]) + if len(file.get_inverse(settings["axis"].AxisCurve)) == 1: + ifcopenshell.util.element.remove_deep(file, settings["axis"].AxisCurve) + file.remove(settings["axis"].AxisCurve) + file.remove(settings["axis"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py index e0caddbe3c..5b729b0dfc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_group import add_group +from .assign_group import assign_group +from .edit_group import edit_group +from .remove_group import remove_group +from .unassign_group import unassign_group +from .update_group_products import update_group_products diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py index 298ec42ba6..1d576f7173 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py @@ -20,44 +20,41 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, Name="Unnamed", Description=None): - """Adds a new group +def add_group(file, Name="Unnamed", Description=None) -> None: + """Adds a new group - An IFC group is an arbitrary collection of products, which are typically - physical. It may be used when there is no other more specific group - which may be used. Other types of groups include distribution systems, - which group together products that are connected and circulate a medium - (such as fluid or electricity), or zones, which group together spaces, - or structural load groups, which group together loads for structural - analysis, or inventories, which are groups of assets. + An IFC group is an arbitrary collection of products, which are typically + physical. It may be used when there is no other more specific group + which may be used. Other types of groups include distribution systems, + which group together products that are connected and circulate a medium + (such as fluid or electricity), or zones, which group together spaces, + or structural load groups, which group together loads for structural + analysis, or inventories, which are groups of assets. - :param Name: The name of the group. Defaults to "Unnamed" - :type Name: str, optional - :param Description: The description of the purpose of the group. - :type Description: str, optional - :return: The newly created IfcGroup - :rtype: ifcopenshell.entity_instance + :param Name: The name of the group. Defaults to "Unnamed" + :type Name: str, optional + :param Description: The description of the purpose of the group. + :type Description: str, optional + :return: The newly created IfcGroup + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - """ - self.file = file - self.settings = { - "Name": Name or "Unnamed", - "Description": Description, + ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + """ + settings = { + "Name": Name or "Unnamed", + "Description": Description, + } + + return file.create_entity( + "IfcGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["Name"], + "Description": settings["Description"], } - - def execute(self): - return self.file.create_entity( - "IfcGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["Name"], - "Description": self.settings["Description"], - } - ) + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py index d312a95bd6..c5afd5b9b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py @@ -21,56 +21,53 @@ import ifcopenshell.api from typing import Union -class Usecase: - def __init__( - self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance - ): - """Assigns products to a group +def assign_group( + file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns products to a group - If a product is already assigned to the group, it will not be assigned - twice. + If a product is already assigned to the group, it will not be assigned + twice. - :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: A list of IfcProduct elements to assign to the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to assign the products to + :type group: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - ifcopenshell.api.run("group.assign_group", model, - products=model.by_type("IfcFurniture"), group=group) - """ - self.file = file - self.settings = { - "products": products, - "group": group, - } + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + ifcopenshell.api.run("group.assign_group", model, + products=model.by_type("IfcFurniture"), group=group) + """ + settings = { + "products": products, + "group": group, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - if not self.settings["group"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": self.settings["products"], - "RelatingGroup": self.settings["group"], - } - ) - rel = self.settings["group"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - products = set(self.settings["products"]) - if products.issubset(related_objects): - return rel - rel.RelatedObjects = list(related_objects | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + if not settings["group"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": settings["products"], + "RelatingGroup": settings["group"], + } + ) + rel = settings["group"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + products = set(settings["products"]) + if products.issubset(related_objects): return rel + rel.RelatedObjects = list(related_objects | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py index 87fa9dcf12..1eb0c8d6f4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, group=None, attributes=None): - """Edits the attributes of an IfcGroup +def edit_group(file, group=None, attributes=None) -> None: + """Edits the attributes of an IfcGroup - For more information about the attributes and data types of an - IfcGroup, consult the IFC documentation. + For more information about the attributes and data types of an + IfcGroup, consult the IFC documentation. - :param group: The IfcGroup entity you want to edit - :type group: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param group: The IfcGroup entity you want to edit + :type group: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - ifcopenshell.api.run("group.edit_group", model, - group=group, attributes={"Description": "All furniture and joinery included in the unit"}) - """ - self.file = file - self.settings = {"group": group, "attributes": attributes or {}} + group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + ifcopenshell.api.run("group.edit_group", model, + group=group, attributes={"Description": "All furniture and joinery included in the unit"}) + """ + settings = {"group": group, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["group"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["group"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py index c87b36e316..05e85f3fc0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py @@ -21,53 +21,50 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, group=None): - """Removes a group +def remove_group(file, group=None) -> None: + """Removes a group - All products assigned to the group will remain, but the relationship to - the group will be removed. + All products assigned to the group will remain, but the relationship to + the group will be removed. - :param group: The IfcGroup entity you want to remove - :type group: ifcopenshell.entity_instance - :return: None - :rtype: None + :param group: The IfcGroup entity you want to remove + :type group: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - ifcopenshell.api.run("group.remove_group", model, group=group) - """ - self.file = file - self.settings = {"group": group} + group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + ifcopenshell.api.run("group.remove_group", model, group=group) + """ + settings = {"group": group} - def execute(self): - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["group"])]: - try: - inverse = self.file.by_id(inverse_id) - except: - continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["group"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToGroup"): - if inverse.RelatingGroup == self.settings["group"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["group"].OwnerHistory - self.file.remove(self.settings["group"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for inverse_id in [i.id() for i in file.get_inverse(settings["group"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["group"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToGroup"): + if inverse.RelatingGroup == settings["group"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["group"].OwnerHistory + file.remove(settings["group"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py index 9229281a69..c486cceab6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py @@ -21,48 +21,47 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance): - """Unassigns products from a group +def unassign_group( + file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance +) -> None: + """Unassigns products from a group - If the product isn't assigned to the group, nothing will happen. + If the product isn't assigned to the group, nothing will happen. - :param products: A list of IfcProduct elements to unassign from the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to unassign from - :type group: ifcopenshell.entity_instance - :return: None - :rtype: None + :param products: A list of IfcProduct elements to unassign from the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to unassign from + :type group: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - furniture = model.by_type("IfcFurniture") - ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group) + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + furniture = model.by_type("IfcFurniture") + ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group) - bad_furniture = furniture[0] - ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group) - """ - self.file = file - self.settings = { - "products": products, - "group": group, - } + bad_furniture = furniture[0] + ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group) + """ + settings = { + "products": products, + "group": group, + } - def execute(self) -> None: - if not self.settings["group"].IsGroupedBy: - return - rel = self.settings["group"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - products = set(self.settings["products"]) - related_objects -= products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if not settings["group"].IsGroupedBy: + return + rel = settings["group"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + products = set(settings["products"]) + related_objects -= products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py index 61b96c2ba6..f526666fa2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py @@ -20,51 +20,48 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, group=None, products=None): - """Sets a group products to be an explicit list of products +def update_group_products(file, group=None, products=None) -> None: + """Sets a group products to be an explicit list of products - Any previous products assigned to that group will have their assignment - removed. + Any previous products assigned to that group will have their assignment + removed. - :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance + :param products: A list of IfcProduct elements to assign to the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to assign the products to + :type group: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - ifcopenshell.api.run("group.update_group_products", model, - products=model.by_type("IfcFurniture"), group=group) - """ - self.file = file - self.settings = { - "group": group, - "products": products, - } + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + ifcopenshell.api.run("group.update_group_products", model, + products=model.by_type("IfcFurniture"), group=group) + """ + settings = { + "group": group, + "products": products, + } - def execute(self): - if not self.settings["group"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": self.settings["products"], - "RelatingGroup": self.settings["group"], - } - ) - else: - # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes - # where the cardinality is 0:? - vulevukusej - rel = self.settings["group"].IsGroupedBy[0] - existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")] + if not settings["group"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": settings["products"], + "RelatingGroup": settings["group"], + } + ) + else: + # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes + # where the cardinality is 0:? - vulevukusej + rel = settings["group"].IsGroupedBy[0] + existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")] - rel.RelatedObjects = self.settings["products"] - for g in existing_sub_groups: - rel.RelatedObjects.add(g) + rel.RelatedObjects = settings["products"] + for g in existing_sub_groups: + rel.RelatedObjects.add(g) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py index e0caddbe3c..03145bf34a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_layer import add_layer +from .assign_layer import assign_layer +from .edit_layer import edit_layer +from .remove_layer import remove_layer +from .unassign_layer import unassign_layer diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py index 8638a76b22..5379ff1b3a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, Name=None): - """Adds a new layer +def add_layer(file, Name=None) -> None: + """Adds a new layer - An IFC layer is like a CAD layer. Portions of an object's geometry - (typically portions of its 2D linework) can be assigned to layers, which - can provide stylistic information such as line weights, colours, or - simply be used for filtering. + An IFC layer is like a CAD layer. Portions of an object's geometry + (typically portions of its 2D linework) can be assigned to layers, which + can provide stylistic information such as line weights, colours, or + simply be used for filtering. - Layers have historically been used to organise CAD data and included in - ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to - be compatible with older, 2D-oriented, layer-based workflows. + Layers have historically been used to organise CAD data and included in + ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to + be compatible with older, 2D-oriented, layer-based workflows. - Some software that are still based on layers, such as Tekla or ArchiCAD - may also use this layer information for filtering. + Some software that are still based on layers, such as Tekla or ArchiCAD + may also use this layer information for filtering. - :param Name: The name of the layer. Defaults to "Unnamed". - :type Name: str, optional - :return: The newly created IfcPresentationLayerAssignment element - :rtype: ifcopenshell.entity_instance + :param Name: The name of the layer. Defaults to "Unnamed". + :type Name: str, optional + :return: The newly created IfcPresentationLayerAssignment element + :rtype: ifcopenshell.entity_instance - Example: + Example: - ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N") - """ - self.file = file - self.settings = {"Name": Name or "Unnamed"} + ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N") + """ + settings = {"Name": Name or "Unnamed"} - def execute(self): - return self.file.create_entity("IfcPresentationLayerAssignment", Name=self.settings["Name"]) + return file.create_entity("IfcPresentationLayerAssignment", Name=settings["Name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py index 93a863d66f..70926625c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py @@ -19,64 +19,61 @@ import ifcopenshell -class Usecase: - def __init__( - self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance - ): - """Assigns representation items to a layer +def assign_layer( + file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance +) -> None: + """Assigns representation items to a layer - In IFC, instead of objects being assigned to layers, representation - items are assigned to layers. Representation items are portions of the - object's representation. For example, this allows a single IFC Window - element to have portions of its 2D linework (e.g. the cross section of - its frame) assigned to one layer, and another portion (e.g. the glazing - panels) assigned to another layer. + In IFC, instead of objects being assigned to layers, representation + items are assigned to layers. Representation items are portions of the + object's representation. For example, this allows a single IFC Window + element to have portions of its 2D linework (e.g. the cross section of + its frame) assigned to one layer, and another portion (e.g. the glazing + panels) assigned to another layer. - :param items: The list of IfcRepresentationItems to assign to the layer. This - should be the items from the object's IfcShapeRepresentation. - :type items: list[ifcopenshell.entity_instance] - :param layer: The IfcPresentationLayerAssignment layer to assign the - item to. - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param items: The list of IfcRepresentationItems to assign to the layer. This + should be the items from the object's IfcShapeRepresentation. + :type items: list[ifcopenshell.entity_instance] + :param layer: The IfcPresentationLayerAssignment layer to assign the + item to. + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remember, all geometry needs to specify the context it is part of first. - # See ifcopenshell.api.context.add_context for details. - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model - ) + # Remember, all geometry needs to specify the context it is part of first. + # See ifcopenshell.api.context.add_context for details. + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model + ) - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Now let's create a layer that contains walls - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + # Now let's create a layer that contains walls + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - # And assign our wall representation item (in this example, there is - # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) - """ - self.file = file - self.settings = { - "items": items, - "layer": layer, - } + # And assign our wall representation item (in this example, there is + # only one item) to the layer. + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) + """ + settings = { + "items": items, + "layer": layer, + } - def execute(self) -> None: - # support AssignedItems == None since layer might just got created - layer = self.settings["layer"] - assigned_items = set(layer.AssignedItems or []) - items = set(self.settings["items"]) - if items.issubset(assigned_items): - return - layer.AssignedItems = list(assigned_items | items) + # support AssignedItems == None since layer might just got created + layer = settings["layer"] + assigned_items = set(layer.AssignedItems or []) + items = set(settings["items"]) + if items.issubset(assigned_items): + return + layer.AssignedItems = list(assigned_items | items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py index c2b1cbc99a..9d96156cfc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None, attributes=None): - """Edits the attributes of an IfcPresentationLayerAssignment +def edit_layer(file, layer=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationLayerAssignment - For more information about the attributes and data types of an - IfcPresentationLayerAssignment, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPresentationLayerAssignment, consult the IFC documentation. - :param layer: The IfcPresentationLayerAssignment entity you want to edit - :type layer: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param layer: The IfcPresentationLayerAssignment entity you want to edit + :type layer: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - ifcopenshell.api.run("layer.edit_layer", model, - layer=layer, attributes={"Description": "All walls, based on the AIA standard."}) - """ - self.file = file - self.settings = {"layer": layer, "attributes": attributes or {}} + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + ifcopenshell.api.run("layer.edit_layer", model, + layer=layer, attributes={"Description": "All walls, based on the AIA standard."}) + """ + settings = {"layer": layer, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["layer"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["layer"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py index 8e83e475ab..790b396174 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py @@ -17,27 +17,24 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None): - """Removes a layer +def remove_layer(file, layer=None) -> None: + """Removes a layer - All representation items assigned to the layer will remain, but the - relationship to the layer will be removed. + All representation items assigned to the layer will remain, but the + relationship to the layer will be removed. - :param layer: The IfcPresentationLayerAssignment entity to remove - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param layer: The IfcPresentationLayerAssignment entity to remove + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - ifcopenshell.api.run("layer.remove_layer", model, layer=layer) - """ - self.file = file - self.settings = {"layer": layer} + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + ifcopenshell.api.run("layer.remove_layer", model, layer=layer) + """ + settings = {"layer": layer} - def execute(self): - self.file.remove(self.settings["layer"]) + file.remove(settings["layer"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py index f9d6a024a3..9418a28ad6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py @@ -20,68 +20,65 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__( - self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance - ): - """Unassigns representation items from a layer +def unassign_layer( + file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance +) -> None: + """Unassigns representation items from a layer - If the representation item isn't assigned to the layer, nothing will - happen. - If after unassignment layer won't have any assigned items it will be - removed to keep IFC valid. + If the representation item isn't assigned to the layer, nothing will + happen. + If after unassignment layer won't have any assigned items it will be + removed to keep IFC valid. - :param items: A list IfcRepresentationItem elements to unassign - :type items: list[ifcopenshell.entity_instance] - :param layer: The IfcPresentationLayerAssignment to unassign from - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param items: A list IfcRepresentationItem elements to unassign + :type items: list[ifcopenshell.entity_instance] + :param layer: The IfcPresentationLayerAssignment to unassign from + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remember, all geometry needs to specify the context it is part of first. - # See ifcopenshell.api.context.add_context for details. - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model - ) + # Remember, all geometry needs to specify the context it is part of first. + # See ifcopenshell.api.context.add_context for details. + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model + ) - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Now let's create a layer that contains walls - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + # Now let's create a layer that contains walls + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - # And assign our wall representation item (in this example, there is - # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) + # And assign our wall representation item (in this example, there is + # only one item) to the layer. + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) - # Let's undo it! - ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer) - """ - self.file = file - self.settings = { - "items": items, - "layer": layer, - } + # Let's undo it! + ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer) + """ + settings = { + "items": items, + "layer": layer, + } - def execute(self): - layer = self.settings["layer"] - assigned_items = set(layer.AssignedItems) or set() - items = set(self.settings["items"]) - if not items.issubset(assigned_items): - return - assigned_items = list(assigned_items - items) + layer = settings["layer"] + assigned_items = set(layer.AssignedItems) or set() + items = set(settings["items"]) + if not items.issubset(assigned_items): + return + assigned_items = list(assigned_items - items) - # keep IFC valid in case if there are no items left - if assigned_items: - layer.AssignedItems = assigned_items - else: - self.file.remove(layer) + # keep IFC valid in case if there are no items left + if assigned_items: + layer.AssignedItems = assigned_items + else: + file.remove(layer) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py index e0caddbe3c..dbb74de3d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py @@ -15,3 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_library import add_library +from .add_reference import add_reference +from .assign_reference import assign_reference +from .edit_library import edit_library +from .edit_reference import edit_reference +from .remove_library import remove_library +from .remove_reference import remove_reference +from .unassign_reference import unassign_reference diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py index f20494ac5f..16cd00b914 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py @@ -21,48 +21,45 @@ import ifcopenshell.util.schema import ifcopenshell.util.date -class Usecase: - def __init__(self, file, name=None): - """Adds a new library to the project +def add_library(file, name=None) -> None: + """Adds a new library to the project - A library is an external data source that is related to the project. It - may be a database, a spreadsheet, an API, or even a stack of papers in a - filing cabinet. This allows IFC data to store relationships to these - external data sources. + A library is an external data source that is related to the project. It + may be a database, a spreadsheet, an API, or even a stack of papers in a + filing cabinet. This allows IFC data to store relationships to these + external data sources. - For example, you may have a list of laser scans of a site stored in an - online platform, which can be queried using an API. Or, you might have a - database of live building sensor data. So long as there is a clear - identifier you can use to link the two datasets together, you can create - a relationship. + For example, you may have a list of laser scans of a site stored in an + online platform, which can be queried using an API. Or, you might have a + database of live building sensor data. So long as there is a clear + identifier you can use to link the two datasets together, you can create + a relationship. - Note that IFC does not store any instructions on how to access the - library. It does not specify whether a HTTP request or database - connection needs to be made or what protocol the library operates with. - Until this is fleshed out further, it is the users responsibility to - name the libraries consistently and use appropriate identifiers. For - example, if you are linking IFC data and Brickschema data, use a full - URI for the identifier with no abbreviation (e.g. - 'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01'). + Note that IFC does not store any instructions on how to access the + library. It does not specify whether a HTTP request or database + connection needs to be made or what protocol the library operates with. + Until this is fleshed out further, it is the users responsibility to + name the libraries consistently and use appropriate identifiers. For + example, if you are linking IFC data and Brickschema data, use a full + URI for the identifier with no abbreviation (e.g. + 'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01'). - A library will then contain a list of references within that library. - These references will then be related to IFC elements. For example, a - library will represent an external database, and a reference will point - to a particular table and row within that database. + A library will then contain a list of references within that library. + These references will then be related to IFC elements. For example, a + library will represent an external database, and a reference will point + to a particular table and row within that database. - :param name: The name of the library - :type name: str - :return: The newly created IfcLibraryInformation - :rtype: ifcopenshell.entity_instance + :param name: The name of the library + :type name: str + :return: The newly created IfcLibraryInformation + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("library.add_library", model, name="Brickschema") - """ - self.file = file - self.settings = {"name": name} + ifcopenshell.api.run("library.add_library", model, name="Brickschema") + """ + settings = {"name": name} - def execute(self): - return self.file.create_entity("IfcLibraryInformation", Name=self.settings["name"]) + return file.create_entity("IfcLibraryInformation", Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py index 84f6605cf0..626d413b3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py @@ -19,48 +19,45 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, library: ifcopenshell.entity_instance): - """Adds a new reference to a library +def add_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: + """Adds a new reference to a library - A library represents an external data source, such as a database, - spreadsheet, API, or something else that contains information related to - the IFC project. Within a library, there will be one or more references, - such as reference to a particular table or row in a database, or a sheet - and row or column in a spreadsheet, a URI in a linked data Brickschema - file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP - address in a network, and so on. + A library represents an external data source, such as a database, + spreadsheet, API, or something else that contains information related to + the IFC project. Within a library, there will be one or more references, + such as reference to a particular table or row in a database, or a sheet + and row or column in a spreadsheet, a URI in a linked data Brickschema + file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP + address in a network, and so on. - These references can then be related to IFC elements. You cannot relate - an IFC element directly to a library, it must be related to one of the - library's references. + These references can then be related to IFC elements. You cannot relate + an IFC element directly to a library, it must be related to one of the + library's references. - :param library: The IfcLibraryInformation element to add a reference to - :type library: ifcopenshell.entity_instance - :return: The newly created IfcLibraryReference element - :rtype: ifcopenshell.entity_instance + :param library: The IfcLibraryInformation element to add a reference to + :type library: ifcopenshell.entity_instance + :return: The newly created IfcLibraryReference element + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - """ - self.file = file - self.settings = { - "library": library, - } + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + """ + settings = { + "library": library, + } - def execute(self) -> ifcopenshell.entity_instance: - if self.file.schema == "IFC2X3": - reference = self.file.createIfcLibraryReference() - references = list(self.settings["library"].LibraryReference or []) - references.append(reference) - self.settings["library"].LibraryReference = references - return reference - return self.file.createIfcLibraryReference(ReferencedLibrary=self.settings["library"]) + if file.schema == "IFC2X3": + reference = file.createIfcLibraryReference() + references = list(settings["library"].LibraryReference or []) + references.append(reference) + settings["library"].LibraryReference = references + return reference + return file.createIfcLibraryReference(ReferencedLibrary=settings["library"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py index 8a0880ccf0..6cbd208865 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py @@ -22,82 +22,75 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance - ): - """Associates a list products with a library reference +def assign_reference( + file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, None]: + """Associates a list products with a library reference - A product may be associated with zero, one, or many references across - multiple libraries. See ifcopenshell.api.library.add_reference for more - detail about how references work. + A product may be associated with zero, one, or many references across + multiple libraries. See ifcopenshell.api.library.add_reference for more + detail about how references work. - :param products: The list of IfcProducts you want to associate with the reference - :type products: list[ifcopenshell.entity_instance] - :param reference: The IfcLibraryReference you want the product to be - associated with. - :type reference: ifcopenshell.entity_instance - :return: The IfcRelAssociatesLibrary relationship entity - or `None` if `products` was an empty list or all products were - already assigned to the `reference`. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of IfcProducts you want to associate with the reference + :type products: list[ifcopenshell.entity_instance] + :param reference: The IfcLibraryReference you want the product to be + associated with. + :type reference: ifcopenshell.entity_instance + :return: The IfcRelAssociatesLibrary relationship entity + or `None` if `products` was an empty list or all products were + already assigned to the `reference`. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - # Let's assume we have an AHU in our model. - ahu = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") + # Let's assume we have an AHU in our model. + ahu = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") - # And now assign the IFC model's AHU with its Brickschema counterpart - ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) - """ - self.file = file - self.settings = { - "products": products, - "reference": reference, - } + # And now assign the IFC model's AHU with its Brickschema counterpart + ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) + """ + settings = { + "products": products, + "reference": reference, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) - products = products - referenced_elements + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + products: set[ifcopenshell.entity_instance] = set(settings["products"]) + products = products - referenced_elements - if not products: - return + if not products: + return - if self.file.schema == "IFC2X3": - rel = next( - ( - r - for r in self.file.by_type("IfcRelAssociatesLibrary") - if r.RelatingLibrary == self.settings["reference"] - ), - None, - ) - else: - rel = next(iter(self.settings["reference"].LibraryRefForObjects), None) + if file.schema == "IFC2X3": + rel = next( + (r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]), + None, + ) + else: + rel = next(iter(settings["reference"].LibraryRefForObjects), None) - if not rel: - return self.file.create_entity( - "IfcRelAssociatesLibrary", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatedObjects=list(products), - RelatingLibrary=self.settings["reference"], - ) + if not rel: + return file.create_entity( + "IfcRelAssociatesLibrary", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatedObjects=list(products), + RelatingLibrary=settings["reference"], + ) - related_objects = set(rel.RelatedObjects) | products - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + related_objects = set(rel.RelatedObjects) | products + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py index aca508cc38..5a53869a3f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, library=None, attributes=None): - """Edits the attributes of an IfcLibraryInformation +def edit_library(file, library=None, attributes=None) -> None: + """Edits the attributes of an IfcLibraryInformation - For more information about the attributes and data types of an - IfcLibraryInformation, consult the IFC documentation. + For more information about the attributes and data types of an + IfcLibraryInformation, consult the IFC documentation. - :param library: The IfcLibraryInformation entity you want to edit - :type library: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param library: The IfcLibraryInformation entity you want to edit + :type library: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - ifcopenshell.api.run("library.edit_library", model, library=library, - attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."}) - """ + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + ifcopenshell.api.run("library.edit_library", model, library=library, + attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."}) + """ - self.file = file - self.settings = {"library": library, "attributes": attributes or {}} + settings = {"library": library, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["library"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["library"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py index 35a1be4709..1d2487820a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, reference=None, attributes=None): - """Edits the attributes of an IfcLibraryReference +def edit_reference(file, reference=None, attributes=None) -> None: + """Edits the attributes of an IfcLibraryReference - For more information about the attributes and data types of an - IfcLibraryReference, consult the IFC documentation. + For more information about the attributes and data types of an + IfcLibraryReference, consult the IFC documentation. - :param reference: The IfcLibraryReference entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcLibraryReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py index e921016c4d..ba5244537c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py @@ -20,35 +20,32 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, library=None): - """Removes a library +def remove_library(file, library=None) -> None: + """Removes a library - All references along with their relationships will also be removed. Any - products which have relationships to this library will not be removed. + All references along with their relationships will also be removed. Any + products which have relationships to this library will not be removed. - :param library: The IfcLibraryInformation entity you want to remove - :type library: ifcopenshell.entity_instance - :return: None - :rtype: None + :param library: The IfcLibraryInformation entity you want to remove + :type library: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - ifcopenshell.api.run("library.remove_library", model, library=library) - """ - self.file = file - self.settings = {"library": library} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + ifcopenshell.api.run("library.remove_library", model, library=library) + """ + settings = {"library": library} - def execute(self): - for reference in set(self.settings["library"].HasLibraryReferences or []): - self.file.remove(reference) - self.file.remove(self.settings["library"]) - for rel in self.file.by_type("IfcRelAssociatesLibrary"): - if not rel.RelatingLibrary: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for reference in set(settings["library"].HasLibraryReferences or []): + file.remove(reference) + file.remove(settings["library"]) + for rel in file.by_type("IfcRelAssociatesLibrary"): + if not rel.RelatingLibrary: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py index d34973f6b2..0b5ad42d1a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py @@ -20,34 +20,31 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance): - """Removes a library reference +def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None: + """Removes a library reference - Any products which have relationships to this reference will not be - removed. + Any products which have relationships to this reference will not be + removed. - :param reference: The IfcLibraryReference entity you want to remove - :type reference: ifcopenshell.entity_instance - :return: None - :rtype: None + :param reference: The IfcLibraryReference entity you want to remove + :type reference: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - # Let's change our mind and remove it. - ifcopenshell.api.run("library.remove_reference", model, reference=reference) - """ - self.file = file - self.settings = {"reference": reference} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + # Let's change our mind and remove it. + ifcopenshell.api.run("library.remove_reference", model, reference=reference) + """ + settings = {"reference": reference} - def execute(self) -> None: - for rel in self.settings["reference"].LibraryRefForObjects: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["reference"]) + for rel in settings["reference"].LibraryRefForObjects: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py index 420b7fa0d4..c2f2837ad0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py @@ -21,70 +21,66 @@ import ifcopenshell.util.element import ifcopenshell.api -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - products: list[ifcopenshell.entity_instance], - ): - """Unassigns a product of products from a reference +def unassign_reference( + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + products: list[ifcopenshell.entity_instance], +) -> None: + """Unassigns a product of products from a reference - If the product isn't assigned to the reference, nothing will happen. + If the product isn't assigned to the reference, nothing will happen. - :param reference: The IfcLibraryReference to unassign from - :type reference: ifcopenshell.entity_instance - :param products: A list of IfcProduct elements to unassign from the reference - :type products: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param reference: The IfcLibraryReference to unassign from + :type reference: ifcopenshell.entity_instance + :param products: A list of IfcProduct elements to unassign from the reference + :type products: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - # Let's assume we have an AHU in our model. - ahu = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") + # Let's assume we have an AHU in our model. + ahu = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") - # And now assign the IFC model's AHU with its Brickschema counterpart - ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) + # And now assign the IFC model's AHU with its Brickschema counterpart + ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) - # Let's change our mind and unassign it. - ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu]) - """ + # Let's change our mind and unassign it. + ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu]) + """ - self.file = file - self.settings = {"reference": reference, "products": products} + settings = {"reference": reference, "products": products} - def execute(self): - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - reference_rels: set[ifcopenshell.entity_instance] = set() - products = set(self.settings["products"]) - for product in products: - reference_rels.update(product.HasAssociations) + reference_rels: set[ifcopenshell.entity_instance] = set() + products = set(settings["products"]) + for product in products: + reference_rels.update(product.HasAssociations) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"] - } + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"] + } - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py index e0caddbe3c..2831915f76 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py @@ -15,3 +15,28 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_constituent import add_constituent +from .add_layer import add_layer +from .add_list_item import add_list_item +from .add_material import add_material +from .add_material_set import add_material_set +from .add_profile import add_profile +from .assign_material import assign_material +from .assign_profile import assign_profile +from .copy_material import copy_material +from .edit_assigned_material import edit_assigned_material +from .edit_constituent import edit_constituent +from .edit_layer import edit_layer +from .edit_layer_usage import edit_layer_usage +from .edit_material import edit_material +from .edit_profile import edit_profile +from .edit_profile_usage import edit_profile_usage +from .remove_constituent import remove_constituent +from .remove_layer import remove_layer +from .remove_list_item import remove_list_item +from .remove_material import remove_material +from .remove_material_set import remove_material_set +from .remove_profile import remove_profile +from .reorder_set_item import reorder_set_item +from .unassign_material import unassign_material diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py index 278eb50872..b1f3f76073 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py @@ -17,75 +17,72 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent_set=None, material=None): - """Adds a new constituent to a constituent set +def add_constituent(file, constituent_set=None, material=None) -> None: + """Adds a new constituent to a constituent set - A constituent describes how a portion of an object is made out of a - material whereas other portions of the object is made out of other - materials. For example, a window might be made out of an aluminium frame - and a glass panel. The aluminium used for the frame is one constituent - of the material, and glass would be another constituent. Another example - might be concrete, where one constituent might be cement, and another - constituent might be binder. In the case of the window, the constituent - is represented explicitly by the geometry of the window frame and the - geometry of the window panel. In the case of a concrete slab, the - constituents might be represented in terms of percentages. + A constituent describes how a portion of an object is made out of a + material whereas other portions of the object is made out of other + materials. For example, a window might be made out of an aluminium frame + and a glass panel. The aluminium used for the frame is one constituent + of the material, and glass would be another constituent. Another example + might be concrete, where one constituent might be cement, and another + constituent might be binder. In the case of the window, the constituent + is represented explicitly by the geometry of the window frame and the + geometry of the window panel. In the case of a concrete slab, the + constituents might be represented in terms of percentages. - Constituents are not available in IFC2X3. + Constituents are not available in IFC2X3. - :param constituent_set: The IfcMaterialConstituentSet that the - constituent is part of. The constituent set represents a group of - constituents. See ifcopenshell.api.material.add_material_set for - information on how to add a constituent set. - :type constituent_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the constituent is made out of. - :type material: ifcopenshell.entity_instance - :return: The newly created IfcMaterialConstituent - :rtype: ifcopenshell.entity_instance + :param constituent_set: The IfcMaterialConstituentSet that the + constituent is part of. The constituent set represents a group of + constituents. See ifcopenshell.api.material.add_material_set for + information on how to add a constituent set. + :type constituent_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the constituent is made out of. + :type material: ifcopenshell.entity_instance + :return: The newly created IfcMaterialConstituent + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a window type that has an aluminium frame - # and a glass glazing panel. Notice we are assigning to the type - # only, as all occurrences of that type will automatically inherit - # the material. - window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") + # Let's imagine we have a window type that has an aluminium frame + # and a glass glazing panel. Notice we are assigning to the type + # only, as all occurrences of that type will automatically inherit + # the material. + window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") - # First, let's create a constituent set. This will later be assigned - # to our window element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # First, let's create a constituent set. This will later be assigned + # to our window element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two constituents in our set. - ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium) - ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Now let's use those materials as two constituents in our set. + ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium) + ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Great! Let's assign our material set to our window type. - # We're technically not done here, we might want to add geometry to - # our window too, but to keep this example simple, geometry is - # optional and it is enough to say that this window is made out of - # aluminium and glass. - ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) - """ - self.file = file - self.settings = {"constituent_set": constituent_set, "material": material} + # Great! Let's assign our material set to our window type. + # We're technically not done here, we might want to add geometry to + # our window too, but to keep this example simple, geometry is + # optional and it is enough to say that this window is made out of + # aluminium and glass. + ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) + """ + settings = {"constituent_set": constituent_set, "material": material} - def execute(self): - constituents = list(self.settings["constituent_set"].MaterialConstituents or []) - constituent = self.file.create_entity("IfcMaterialConstituent", **{"Material": self.settings["material"]}) - constituents.append(constituent) - self.settings["constituent_set"].MaterialConstituents = constituents - return constituent + constituents = list(settings["constituent_set"].MaterialConstituents or []) + constituent = file.create_entity("IfcMaterialConstituent", **{"Material": settings["material"]}) + constituents.append(constituent) + settings["constituent_set"].MaterialConstituents = constituents + return constituent diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py index aa572f07bd..68885715ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py @@ -17,75 +17,70 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer_set=None, material=None): - """Adds a new layer to a layer set +def add_layer(file, layer_set=None, material=None) -> None: + """Adds a new layer to a layer set - A layer represents a portion of material within a layered build up, - defined by a thickness. Typical layered construction includes walls and - slabs, where a wall might include a layer of finish, a layer of - structure, a layer of insulation, and so on. It is recommended to define - layered construction this way where it is unnecessary to define the - exact geometry of how the wall or slab will be built, and it will - instead be determined on site by a trade. + A layer represents a portion of material within a layered build up, + defined by a thickness. Typical layered construction includes walls and + slabs, where a wall might include a layer of finish, a layer of + structure, a layer of insulation, and so on. It is recommended to define + layered construction this way where it is unnecessary to define the + exact geometry of how the wall or slab will be built, and it will + instead be determined on site by a trade. - Layers are defined in a particular order and thickness, so that it is - clear which layer comes next. + Layers are defined in a particular order and thickness, so that it is + clear which layer comes next. - :param layer_set: The IfcMaterialLayerSet that the layer is part of. The - layer set represents a group of layers. See - ifcopenshell.api.material.add_material_set for more information on - how to add a layer set. - :type layer_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the layer is made out of. - :type material: ifcopenshell.entity_instance - :return: The newly created IfcMaterialLayer - :rtype: ifcopenshell.entity_instance + :param layer_set: The IfcMaterialLayerSet that the layer is part of. The + layer set represents a group of layers. See + ifcopenshell.api.material.add_material_set for more information on + how to add a layer set. + :type layer_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the layer is made out of. + :type material: ifcopenshell.entity_instance + :return: The newly created IfcMaterialLayer + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a wall type that has two layers of - # gypsum with steel studs inside. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # Let's imagine we have a wall type that has two layers of + # gypsum with steel studs inside. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - """ - self.file = file - self.settings = {"layer_set": layer_set, "material": material} + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + """ + settings = {"layer_set": layer_set, "material": material} - def execute(self): - layers = list(self.settings["layer_set"].MaterialLayers or []) - layer = self.file.create_entity( - "IfcMaterialLayer", **{"Material": self.settings["material"], "LayerThickness": 1.0} - ) - layers.append(layer) - self.settings["layer_set"].MaterialLayers = layers - return layer + layers = list(settings["layer_set"].MaterialLayers or []) + layer = file.create_entity("IfcMaterialLayer", **{"Material": settings["material"], "LayerThickness": 1.0}) + layers.append(layer) + settings["layer_set"].MaterialLayers = layers + return layer diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py index 9a12ed044b..7eaa8159ce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py @@ -19,70 +19,67 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material_list=None, material=None): - """Adds a new material in a list of materials +def add_list_item(file, material_list=None, material=None) -> None: + """Adds a new material in a list of materials - In IFC2X3, if you wanted an object to have multiple materials (i.e. a - composite material) you would assign the object to a material list, - which would contain a list of materials. For example, a window might - have a list of 2 materials, one being aluminium for the frame, and - another being glass for the panel. + In IFC2X3, if you wanted an object to have multiple materials (i.e. a + composite material) you would assign the object to a material list, + which would contain a list of materials. For example, a window might + have a list of 2 materials, one being aluminium for the frame, and + another being glass for the panel. - In IFC4 and above, this is deprecated and should not be used. Instead, - you should use constituent sets instead, which achieve the same thing - but are more powerful as they allow you to define the properties of the - constituents too. + In IFC4 and above, this is deprecated and should not be used. Instead, + you should use constituent sets instead, which achieve the same thing + but are more powerful as they allow you to define the properties of the + constituents too. - However if you're stuck on IFC2X3, you have my condolences as well as - this function. + However if you're stuck on IFC2X3, you have my condolences as well as + this function. - :param material_list: The IfcMaterialList the material should be added - to. - :type material_list: ifcopenshell.entity_instance - :param material: The IfcMaterial to add to the list - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material_list: The IfcMaterialList the material should be added + to. + :type material_list: ifcopenshell.entity_instance + :param material: The IfcMaterial to add to the list + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a window type that has an aluminium frame - # and a glass glazing panel. Notice we are assigning to the type - # only, as all occurrences of that type will automatically inherit - # the material. - window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") + # Let's imagine we have a window type that has an aluminium frame + # and a glass glazing panel. Notice we are assigning to the type + # only, as all occurrences of that type will automatically inherit + # the material. + window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") - # First, let's create a list. This will later be assigned to our - # window element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialList") + # First, let's create a list. This will later be assigned to our + # window element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialList") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Great! Let's assign our material set to our window type. - # We're technically not done here, we might want to add geometry to - # our window too, but to keep this example simple, geometry is - # optional and it is enough to say that this window is made out of - # aluminium and glass. - ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) - """ - self.file = file - self.settings = {"material_list": material_list, "material": material} + # Great! Let's assign our material set to our window type. + # We're technically not done here, we might want to add geometry to + # our window too, but to keep this example simple, geometry is + # optional and it is enough to say that this window is made out of + # aluminium and glass. + ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) + """ + settings = {"material_list": material_list, "material": material} - def execute(self): - materials = list(self.settings["material_list"].Materials or []) - materials.append(self.settings["material"]) - self.settings["material_list"].Materials = materials + materials = list(settings["material_list"].Materials or []) + materials.append(settings["material"]) + settings["material_list"].Materials = materials diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py index bac5a3ac0a..534d9e911c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py @@ -17,64 +17,61 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, category=None): - """Adds a new material +def add_material(file, name=None, category=None) -> None: + """Adds a new material - A material in IFC represents a physical material, such as timber, steel, - concrete, aluminium, etc. It may also contain physical properties used - for structural or lighting simulation. Note that unlike the computer - graphics industry, a material by itself does not define any colour or - lighting information. Colours in IFC are known as "styles", and an IFC - material may or may not have any style information associated with it. - See ifcopenshell.api.style for more information. + A material in IFC represents a physical material, such as timber, steel, + concrete, aluminium, etc. It may also contain physical properties used + for structural or lighting simulation. Note that unlike the computer + graphics industry, a material by itself does not define any colour or + lighting information. Colours in IFC are known as "styles", and an IFC + material may or may not have any style information associated with it. + See ifcopenshell.api.style for more information. - A material is typically given a code name which is used by architects in - elevations and details when tagging finishes. Materials are also useful - to structural engineers in specifying the exact types of concrete and - steel to be used in structural simulations. + A material is typically given a code name which is used by architects in + elevations and details when tagging finishes. Materials are also useful + to structural engineers in specifying the exact types of concrete and + steel to be used in structural simulations. - In addition, materials can belong to a category. Specifying this - category is critical to allow model recipients to make simple queries - like "show me all concrete / steel" elements in the model. Without - standardised category naming of all materials, this type of query - becomes a bespoke and inefficient task. A list of categories are: - 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood', - 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to - specify their own category instead if none of these categories are - appropriate. + In addition, materials can belong to a category. Specifying this + category is critical to allow model recipients to make simple queries + like "show me all concrete / steel" elements in the model. Without + standardised category naming of all materials, this type of query + becomes a bespoke and inefficient task. A list of categories are: + 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood', + 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to + specify their own category instead if none of these categories are + appropriate. - Note that categories are not available in IFC2X3. This shortcoming is - one of the big reasons projects should upgrade to IFC4. + Note that categories are not available in IFC2X3. This shortcoming is + one of the big reasons projects should upgrade to IFC4. - :param name: The name of the material, typically tagged in a finishes - drawing or schedule. - :type name: str - :param category: The category of the material. - :type category: str, optional - :return: The newly created IfcMaterial - :rtype: ifcopenshell.entity_instance + :param name: The name of the material, typically tagged in a finishes + drawing or schedule. + :type name: str + :param category: The category of the material. + :type category: str, optional + :return: The newly created IfcMaterial + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create two materials with their respective categories - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create two materials with their respective categories + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Let's imagine an urban concrete bench which is purely made out of concrete - concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + # Let's imagine an urban concrete bench which is purely made out of concrete + concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - # Assign the concrete material to that bench. Note that no colour - # "Style" has been specified. - ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete) - """ - self.file = file - self.settings = {"name": name or "Unnamed", "category": category} + # Assign the concrete material to that bench. Note that no colour + # "Style" has been specified. + ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete) + """ + settings = {"name": name or "Unnamed", "category": category} - def execute(self): - material = self.file.create_entity("IfcMaterial", **{"Name": self.settings["name"] or "Unnamed"}) - if self.settings["category"]: - material.Category = self.settings["category"] - return material + material = file.create_entity("IfcMaterial", **{"Name": settings["name"] or "Unnamed"}) + if settings["category"]: + material.Category = settings["category"] + return material diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py index 277aa258f2..99cfafad41 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py @@ -17,97 +17,94 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name="Unnamed", set_type="IfcMaterialConstituentSet"): - """Adds a new material set +def add_material_set(file, name="Unnamed", set_type="IfcMaterialConstituentSet") -> None: + """Adds a new material set - IFC allows you to state that objects are made out of multiple materials. - These are known generically as material sets, but may also be called - layered materials, composite materials, or other names in software. + IFC allows you to state that objects are made out of multiple materials. + These are known generically as material sets, but may also be called + layered materials, composite materials, or other names in software. - There are three types of material sets: + There are three types of material sets: - - A layer set, used for layered construction such as walls, where the - element is parametrically made out of extruded layers, each layer - having a thickness defined. Even though this is known as a layer - "set" it is still recommended to use it for all standared layered - construction as it describes the intent of the element to be layered - construction and thus can be used for parametric editing. - - A profile set, used for profiled construction such as beams or - columns, where the element is parametrically made out of one or more - extruded profiles, where each profile may be parametric from a - standard section (e.g. standardised steel profile) or an arbitrary - shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note - that even though this is called a profile "set", it should still be - used even if there is only a single profile. This is not available in - IFC2X3. - - A constituent set, used for arbitrary composite construction where - the object is made out of multiple materials. The constituents may be - explicitly defined via a shape, such as a window where the frame - geometry is made from one material and the panel geometry is made - from another material. Alternatively, the constituents may be - represented in terms of percentages, such as in mixtures like - concrete where there might be a percentage constituent of cement and - another percentage constituent of binder. This is not available in - IFC2X3. + - A layer set, used for layered construction such as walls, where the + element is parametrically made out of extruded layers, each layer + having a thickness defined. Even though this is known as a layer + "set" it is still recommended to use it for all standared layered + construction as it describes the intent of the element to be layered + construction and thus can be used for parametric editing. + - A profile set, used for profiled construction such as beams or + columns, where the element is parametrically made out of one or more + extruded profiles, where each profile may be parametric from a + standard section (e.g. standardised steel profile) or an arbitrary + shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note + that even though this is called a profile "set", it should still be + used even if there is only a single profile. This is not available in + IFC2X3. + - A constituent set, used for arbitrary composite construction where + the object is made out of multiple materials. The constituents may be + explicitly defined via a shape, such as a window where the frame + geometry is made from one material and the panel geometry is made + from another material. Alternatively, the constituents may be + represented in terms of percentages, such as in mixtures like + concrete where there might be a percentage constituent of cement and + another percentage constituent of binder. This is not available in + IFC2X3. - There is also a fourth material set known as a material list, which is a - legacy type of set used by IFC2X3. It should not be used on IFC4 and - above, and constituent sets should be used instead. + There is also a fourth material set known as a material list, which is a + legacy type of set used by IFC2X3. It should not be used on IFC4 and + above, and constituent sets should be used instead. - :param name: The name of the material set, which may be purely - descriptive or annotated in drawings. Defaults to "Unnamed". - :type name: str, optional - :param set_type: What type of set you want to create, chosen from - IfcMaterialLayerSet, IfcMaterialProfileSet, - IfcMaterialConstituentSet, or IfcMaterialList. Defaults to - IfcMaterialConstituentSet. - :type set_type: str, optional - :return: The newly created material set element - :rtype: ifcopenshell.entity_instance + :param name: The name of the material set, which may be purely + descriptive or annotated in drawings. Defaults to "Unnamed". + :type name: str, optional + :param set_type: What type of set you want to create, chosen from + IfcMaterialLayerSet, IfcMaterialProfileSet, + IfcMaterialConstituentSet, or IfcMaterialList. Defaults to + IfcMaterialConstituentSet. + :type set_type: str, optional + :return: The newly created material set element + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a wall type that has two layers of - # gypsum with steel studs inside. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # Let's imagine we have a wall type that has two layers of + # gypsum with steel studs inside. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - """ - self.file = file - self.settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"} + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + """ + settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"} - def execute(self): - if self.settings["set_type"] == "IfcMaterialLayerSet": - return self.file.create_entity("IfcMaterialLayerSet", LayerSetName=self.settings["name"] or "Unnamed") - elif self.settings["set_type"] == "IfcMaterialList": - return self.file.create_entity("IfcMaterialList") - return self.file.create_entity(self.settings["set_type"], Name=self.settings["name"] or "Unnamed") + if settings["set_type"] == "IfcMaterialLayerSet": + return file.create_entity("IfcMaterialLayerSet", LayerSetName=settings["name"] or "Unnamed") + elif settings["set_type"] == "IfcMaterialList": + return file.create_entity("IfcMaterialList") + return file.create_entity(settings["set_type"], Name=settings["name"] or "Unnamed") diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py index ff2cf3bed5..3a23dd44a9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py @@ -19,86 +19,82 @@ import ifcopenshell from typing import Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - profile_set: ifcopenshell.entity_instance, - material: Optional[ifcopenshell.entity_instance] = None, - profile: Optional[ifcopenshell.entity_instance] = None, - ): - """Add a new profile item to a profile set +def add_profile( + file: ifcopenshell.file, + profile_set: ifcopenshell.entity_instance, + material: Optional[ifcopenshell.entity_instance] = None, + profile: Optional[ifcopenshell.entity_instance] = None, +) -> ifcopenshell.entity_instance: + """Add a new profile item to a profile set - A profile item in a profile set represents an extruded 2D profile curve - that is extruded along the axis of the element. Most commonly there will - only be a single profile item in a profile set. For example, a beam will - have a material profile set containing a single profile item, which may - have a steel material and a I-beam shaped profile curve. + A profile item in a profile set represents an extruded 2D profile curve + that is extruded along the axis of the element. Most commonly there will + only be a single profile item in a profile set. For example, a beam will + have a material profile set containing a single profile item, which may + have a steel material and a I-beam shaped profile curve. - Note that the "profile item" represents a single extrusion in the - profile set, whereas the "profile curve" represents a 2D curve used by a - "profile item". + Note that the "profile item" represents a single extrusion in the + profile set, whereas the "profile curve" represents a 2D curve used by a + "profile item". - In some cases, a profiled element (i.e. beam, column) may be a composite - beam or column and include multiple extrusions. This is rare. The order - of the profiles does not matter. + In some cases, a profiled element (i.e. beam, column) may be a composite + beam or column and include multiple extrusions. This is rare. The order + of the profiles does not matter. - :param profile_set: The IfcMaterialProfileSet that the profile is part of. The - profile set represents a group of profile items. See - ifcopenshell.api.material.add_material_set for more information on - how to add a profile set. - :type profile_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the profile item is made out of. - :type material: ifcopenshell.entity_instance, optional - :param profile: The IfcProfileDef that represents the 2D cross section - of the the profile item. - :type profile: ifcopenshell.entity_instance, optional - :return: The newly created IfcMaterialProfile - :rtype: ifcopenshell.entity_instance + :param profile_set: The IfcMaterialProfileSet that the profile is part of. The + profile set represents a group of profile items. See + ifcopenshell.api.material.add_material_set for more information on + how to add a profile set. + :type profile_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the profile item is made out of. + :type material: ifcopenshell.entity_instance, optional + :param profile: The IfcProfileDef that represents the 2D cross section + of the the profile item. + :type profile: ifcopenshell.entity_instance, optional + :return: The newly created IfcMaterialProfile + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - """ - self.file = file - self.settings = {"profile_set": profile_set, "material": material, "profile": profile} + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + """ + settings = {"profile_set": profile_set, "material": material, "profile": profile} - def execute(self) -> ifcopenshell.entity_instance: - profiles = list(self.settings["profile_set"].MaterialProfiles or []) - profile = self.file.create_entity("IfcMaterialProfile") - if self.settings["material"]: - profile.Material = self.settings["material"] - if self.settings["profile"]: - profile.Profile = self.settings["profile"] - profiles.append(profile) - self.settings["profile_set"].MaterialProfiles = profiles - return profile + profiles = list(settings["profile_set"].MaterialProfiles or []) + profile = file.create_entity("IfcMaterialProfile") + if settings["material"]: + profile.Material = settings["material"] + if settings["profile"]: + profile.Profile = settings["profile"] + profiles.append(profile) + settings["profile_set"].MaterialProfiles = profiles + return profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py index a65a644866..9098ca72fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py @@ -23,133 +23,135 @@ import ifcopenshell.util.representation from typing import Optional, Union +def assign_material( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + type: str = "IfcMaterial", + material: Optional[ifcopenshell.entity_instance] = None, +) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]: + """Assigns a material to the list of products + + Will unassign previously assigned material. + + When a material is assigned to a product, it means that the product is + made out of that material. In its simplest form, a single material may + be assigned to a product, meaning that the entire product is made out of + that one material. Alternatively, a material set may be assigned to a + product, meaning that the product is made out of a set of materials. + There are three types of sets, including layered construction, profiled + materials, and arbitrary material constituents. See + ifcopenshell.api.material.add_material_set for details. + + Materials are typically assigned to the element types rather than + individual occurrences of elements. Individual occurrences would then + inherit the material from the type. + + If the type has a material set, then the geometry of the occurrences + must comply with the material set. For example, if the type has a + constituent set, then it is expected that all occurrences also inherit + the geometry of the type, which is made out of those constituents. + Alternatively, if the type has a layer set, then all occurrences must + have geometry that has a thickness equal to the sum of all layers. If a + type has a profile set, then all occurrences must has the same profile + extruded along its axis. + + For layers and profiles assigned to types, the occurrences must be + assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage. + This allows individual occurrences to override the layered or profiled + construction offset from a reference line. + + :param products: The list of IfcProducts to assign the material or material set + to. + :type products: list[ifcopenshell.entity_instance] + :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", + "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", + "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or + "IfcMaterialList". Note that "Set Usages" may only be assigned to + occurrences, not types. Defaults to "IfcMaterial". + :type type: str + :param material: The IfcMaterial or material set you are assigning here. + If type is Usage then no need to provide `material`, it will be deduced + from the element type automatically. + :type material: ifcopenshell.entity_instance, optional + :return: IfcRelAssociatesMaterial entity + or a list of IfcRelAssociatesMaterial entities + (possible if `type` is Usage + and `products` require different Usages) + or `None` if `products` was empty list. + :rtype: Union[ + ifcopenshell.entity_instance, + list[ifcopenshell.entity_instance], None] + + Example: + + .. code:: python + + # Let's start with a simple concrete material + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Let's imagine a concrete bench made out of a single concrete + # material. Let's assign it to the type. + bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + ifcopenshell.api.run("material.assign_material", model, + products=[bench_type], type="IfcMaterial", material=concrete) + + # Let's imagine there are a two occurrences of this bench. It's not + # necessary to assign any material to these benches as they + # automatically inherit the material from the type. + bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type) + ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type) + + # If we have a concrete wall, we should use a layer set. Again, + # let's start with a wall type, not occurrences. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + + # Even though there is only one layer in our layer set, we still use + # a layer set because it makes it clear that this is a layered + # construction. Let's say it's a 200mm thick concrete layer. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="CON200", set_type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) + + # Our wall type now has the layer set assigned to it + ifcopenshell.api.run("material.assign_material", model, + products=[wall_type], type="IfcMaterialLayerSet", material=material_set) + + # Let's imagine an occurrence of this wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + + # Our wall occurrence needs to have a "set usage" which describes + # how the layers relate to a reference line (typically a 2D line + # representing the extents of the wall). Usages are special since + # they automatically detect the inherited material set from the + # type. You'd write similar code for a profile set. + ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterialLayerSetUsage") + + # To be complete, let's create the wall's axis and body + # representation. Notice how the axis guides the walls "reference + # line" which determines where layers are extruded from, and the + # body has a thickness of 200mm, same as our total layer set + # thickness. + axis = ifcopenshell.api.run("geometry.add_axis_representation", model, + context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)]) + body = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body_context, length=5000, height=3000, thickness=200) + ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis) + ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"products": products, "type": type, "material": material} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - type: str = "IfcMaterial", - material: Optional[ifcopenshell.entity_instance] = None, - ): - """Assigns a material to the list of products - - Will unassign previously assigned material. - - When a material is assigned to a product, it means that the product is - made out of that material. In its simplest form, a single material may - be assigned to a product, meaning that the entire product is made out of - that one material. Alternatively, a material set may be assigned to a - product, meaning that the product is made out of a set of materials. - There are three types of sets, including layered construction, profiled - materials, and arbitrary material constituents. See - ifcopenshell.api.material.add_material_set for details. - - Materials are typically assigned to the element types rather than - individual occurrences of elements. Individual occurrences would then - inherit the material from the type. - - If the type has a material set, then the geometry of the occurrences - must comply with the material set. For example, if the type has a - constituent set, then it is expected that all occurrences also inherit - the geometry of the type, which is made out of those constituents. - Alternatively, if the type has a layer set, then all occurrences must - have geometry that has a thickness equal to the sum of all layers. If a - type has a profile set, then all occurrences must has the same profile - extruded along its axis. - - For layers and profiles assigned to types, the occurrences must be - assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage. - This allows individual occurrences to override the layered or profiled - construction offset from a reference line. - - :param products: The list of IfcProducts to assign the material or material set - to. - :type products: list[ifcopenshell.entity_instance] - :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", - "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", - "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or - "IfcMaterialList". Note that "Set Usages" may only be assigned to - occurrences, not types. Defaults to "IfcMaterial". - :type type: str - :param material: The IfcMaterial or material set you are assigning here. - If type is Usage then no need to provide `material`, it will be deduced - from the element type automatically. - :type material: ifcopenshell.entity_instance, optional - :return: IfcRelAssociatesMaterial entity - or a list of IfcRelAssociatesMaterial entities - (possible if `type` is Usage - and `products` require different Usages) - or `None` if `products` was empty list. - :rtype: Union[ - ifcopenshell.entity_instance, - list[ifcopenshell.entity_instance], None] - - Example: - - .. code:: python - - # Let's start with a simple concrete material - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Let's imagine a concrete bench made out of a single concrete - # material. Let's assign it to the type. - bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - ifcopenshell.api.run("material.assign_material", model, - products=[bench_type], type="IfcMaterial", material=concrete) - - # Let's imagine there are a two occurrences of this bench. It's not - # necessary to assign any material to these benches as they - # automatically inherit the material from the type. - bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type) - ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type) - - # If we have a concrete wall, we should use a layer set. Again, - # let's start with a wall type, not occurrences. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - - # Even though there is only one layer in our layer set, we still use - # a layer set because it makes it clear that this is a layered - # construction. Let's say it's a 200mm thick concrete layer. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="CON200", set_type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) - - # Our wall type now has the layer set assigned to it - ifcopenshell.api.run("material.assign_material", model, - products=[wall_type], type="IfcMaterialLayerSet", material=material_set) - - # Let's imagine an occurrence of this wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - - # Our wall occurrence needs to have a "set usage" which describes - # how the layers relate to a reference line (typically a 2D line - # representing the extents of the wall). Usages are special since - # they automatically detect the inherited material set from the - # type. You'd write similar code for a profile set. - ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterialLayerSetUsage") - - # To be complete, let's create the wall's axis and body - # representation. Notice how the axis guides the walls "reference - # line" which determines where layers are extruded from, and the - # body has a thickness of 200mm, same as our total layer set - # thickness. - axis = ifcopenshell.api.run("geometry.add_axis_representation", model, - context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)]) - body = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body_context, length=5000, height=3000, thickness=200) - ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis) - ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = {"products": products, "type": type, "material": material} - - def execute(self) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]: + def execute(self): self.products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) if not self.products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 4d1678a0ae..15c7e4d779 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -19,78 +19,81 @@ import ifcopenshell.util.representation +def assign_profile(file, material_profile=None, profile=None) -> None: + """Changes the profile curve of a material profile item in a profile set + + In addition to changing the profile curve, it will also change the + profile curve used in any body representation extrusions. + + :param material_profile: The IfcMaterialProfile to change the profile + curve of. See ifcopenshell.api.material.add_profile to see how to + create profiles. + :type material_profile: ifcopenshell.entity_instance + :param profile: The IfcProfileDef to set the profile item's curve to. + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") + + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) + + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + + # Let's create an occurrence of this beam. + beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") + ifcopenshell.api.run("material.assign_material", model, + products=[beam], type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geometry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) + + # Now let's change the profile to a HEA200 standard profile instead. + # This will automatically change the body representation that we + # just added as well to a HEA200 profile. + hea200 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", + OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, + ) + ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"material_profile": material_profile, "profile": profile} + return usecase.execute() + + class Usecase: - def __init__(self, file, material_profile=None, profile=None): - """Changes the profile curve of a material profile item in a profile set - - In addition to changing the profile curve, it will also change the - profile curve used in any body representation extrusions. - - :param material_profile: The IfcMaterialProfile to change the profile - curve of. See ifcopenshell.api.material.add_profile to see how to - create profiles. - :type material_profile: ifcopenshell.entity_instance - :param profile: The IfcProfileDef to set the profile item's curve to. - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") - - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) - - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - - # Let's create an occurrence of this beam. - beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") - ifcopenshell.api.run("material.assign_material", model, - products=[beam], type="IfcMaterialProfileSetUsage") - - # Let's give a 1000mm long beam body representation. - body = ifcopenshell.api.run("geometry.add_profile_representation", - context=body_context, profile=hea100, depth=1000) - ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) - - # Now let's change the profile to a HEA200 standard profile instead. - # This will automatically change the body representation that we - # just added as well to a HEA200 profile. - hea200 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", - OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, - ) - ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200) - """ - self.file = file - self.settings = {"material_profile": material_profile, "profile": profile} - def execute(self): # TODO: handle composite profiles old_profile = self.settings["material_profile"].Profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py index 8dac43b8e0..c862e9cb4b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py @@ -20,45 +20,42 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Copies a material +def copy_material(file, material=None) -> None: + """Copies a material - All material psets and styles are copied. The copied material is not - associated to any elements. + All material psets and styles are copied. The copied material is not + associated to any elements. - :param material: The IfcMaterial to copy - :type material: ifcopenshell.entity_instance - :return: The new copy of the material - :rtype: ifcopenshell.entity_instance + :param material: The IfcMaterial to copy + :type material: ifcopenshell.entity_instance + :return: The new copy of the material + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - # Let's duplicate the concrete material - concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete) - """ - self.file = file - self.settings = {"material": material} + # Let's duplicate the concrete material + concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete) + """ + settings = {"material": material} - def execute(self): - if self.settings["material"].is_a("IfcMaterial"): - new = ifcopenshell.util.element.copy(self.file, self.settings["material"]) - for inverse in self.file.get_inverse(self.settings["material"]): - if inverse.is_a("IfcMaterialProperties"): - # Properties must not be shared between objects for convenience of authoring - inverse = ifcopenshell.util.element.copy(self.file, inverse) - properties = [] - for pset in inverse.Properties: - properties.append(ifcopenshell.util.element.copy_deep(self.file, pset)) - inverse.Properties = properties - inverse.Material = new - elif inverse.is_a("IfcMaterialDefinitionRepresentation"): - inverse = ifcopenshell.util.element.copy_deep( - self.file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"] - ) - inverse.RepresentedMaterial = new - return new + if settings["material"].is_a("IfcMaterial"): + new = ifcopenshell.util.element.copy(file, settings["material"]) + for inverse in file.get_inverse(settings["material"]): + if inverse.is_a("IfcMaterialProperties"): + # Properties must not be shared between objects for convenience of authoring + inverse = ifcopenshell.util.element.copy(file, inverse) + properties = [] + for pset in inverse.Properties: + properties.append(ifcopenshell.util.element.copy_deep(file, pset)) + inverse.Properties = properties + inverse.Material = new + elif inverse.is_a("IfcMaterialDefinitionRepresentation"): + inverse = ifcopenshell.util.element.copy_deep( + file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"] + ) + inverse.RepresentedMaterial = new + return new diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py index 3e3a03dbd8..3102d5a645 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, element=None, attributes=None): - """Edits the attributes of an IfcMaterial +def edit_assigned_material(file, element=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterial - For more information about the attributes and data types of an - IfcMaterial, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterial, consult the IFC documentation. - :param element: The IfcMaterial entity you want to edit - :type element: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param element: The IfcMaterial entity you want to edit + :type element: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - ifcopenshell.api.run("material.edit_assigned_material", model, - element=concrete, attributes={"Description": "40MPA concrete with broom finish"}) - """ - self.file = file - self.settings = {"element": element, "attributes": attributes or {}} + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + ifcopenshell.api.run("material.edit_assigned_material", model, + element=concrete, attributes={"Description": "40MPA concrete with broom finish"}) + """ + settings = {"element": element, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["element"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["element"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py index ef036527a3..998bef98bd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py @@ -17,52 +17,49 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent=None, attributes=None, material=None): - """Edits the attributes of an IfcMaterialConstituent +def edit_constituent(file, constituent=None, attributes=None, material=None) -> None: + """Edits the attributes of an IfcMaterialConstituent - For more information about the attributes and data types of an - IfcMaterialConstituent, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialConstituent, consult the IFC documentation. - :param constituent: The IfcMaterialConstituent entity you want to edit - :type constituent: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param material: The IfcMaterial entity you want to change the constituent to - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param constituent: The IfcMaterialConstituent entity you want to edit + :type constituent: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param material: The IfcMaterial entity you want to change the constituent to + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's add two materials - aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's add two materials + aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - # Set up two constituents, one for the frame and the other for the glazing. - framing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium1) - glazing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Set up two constituents, one for the frame and the other for the glazing. + framing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium1) + glazing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Let's make sure this constituent refers to the framing of the - # window and uses the second aluminium material instead. - ifcopenshell.api.run("material.edit_constituent", model, - constituent=framing, attributes={"Name": "Framing"}, material=aluminium2) + # Let's make sure this constituent refers to the framing of the + # window and uses the second aluminium material instead. + ifcopenshell.api.run("material.edit_constituent", model, + constituent=framing, attributes={"Name": "Framing"}, material=aluminium2) - ifcopenshell.api.run("material.edit_constituent", model, - constituent=constituent, attributes={"Name": "Glazing"}) - """ - self.file = file - self.settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} + ifcopenshell.api.run("material.edit_constituent", model, + constituent=constituent, attributes={"Name": "Glazing"}) + """ + settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["constituent"], name, value) - self.settings["constituent"].Material = self.settings["material"] + for name, value in settings["attributes"].items(): + setattr(settings["constituent"], name, value) + settings["constituent"].Material = settings["material"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py index 3e31194452..78ce15132f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py @@ -17,51 +17,48 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None, attributes=None, material=None): - """Edits the attributes of an IfcMaterialLayer +def edit_layer(file, layer=None, attributes=None, material=None) -> None: + """Edits the attributes of an IfcMaterialLayer - For more information about the attributes and data types of an - IfcMaterialLayer, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialLayer, consult the IFC documentation. - :param layer: The IfcMaterialLayer entity you want to edit - :type layer: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param material: The IfcMaterial entity you want the layer to be made - from. - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param layer: The IfcMaterialLayer entity you want to edit + :type layer: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param material: The IfcMaterial entity you want the layer to be made + from. + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create two materials typically used for steel stud partition - # walls with gypsum lining. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create two materials typically used for steel stud partition + # walls with gypsum lining. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create a material layer set to contain our layers. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # Create a material layer set to contain our layers. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - """ - self.file = file - self.settings = {"layer": layer, "attributes": attributes or {}, "material": material} + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + """ + settings = {"layer": layer, "attributes": attributes or {}, "material": material} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["layer"], name, value) - if self.settings["material"]: - self.settings["layer"].Material = self.settings["material"] + for name, value in settings["attributes"].items(): + setattr(settings["layer"], name, value) + if settings["material"]: + settings["layer"].Material = settings["material"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py index a30fa39f21..9204728004 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py @@ -17,66 +17,63 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, usage=None, attributes=None): - """Edits the attributes of an IfcMaterialLayerSetUsage +def edit_layer_usage(file, usage=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterialLayerSetUsage - This is typically used to change the offset from the reference line to - the layers. + This is typically used to change the offset from the reference line to + the layers. - For more information about the attributes and data types of an - IfcMaterialLayerSetUsage, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialLayerSetUsage, consult the IFC documentation. - :param usage: The IfcMaterialLayerSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param usage: The IfcMaterialLayerSetUsage entity you want to edit + :type usage: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's start with a simple concrete material - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + # Let's start with a simple concrete material + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - # If we have a concrete wall, we should use a layer set. Again, - # let's start with a wall type, not occurrences. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # If we have a concrete wall, we should use a layer set. Again, + # let's start with a wall type, not occurrences. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # Even though there is only one layer in our layer set, we still use - # a layer set because it makes it clear that this is a layered - # construction. Let's say it's a 200mm thick concrete layer. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="CON200", set_type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) + # Even though there is only one layer in our layer set, we still use + # a layer set because it makes it clear that this is a layered + # construction. Let's say it's a 200mm thick concrete layer. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="CON200", set_type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) - # Our wall type now has the layer set assigned to it - ifcopenshell.api.run("material.assign_material", model, - products=[wall_type], type="IfcMaterialLayerSet", material=material_set) + # Our wall type now has the layer set assigned to it + ifcopenshell.api.run("material.assign_material", model, + products=[wall_type], type="IfcMaterialLayerSet", material=material_set) - # Let's imagine an occurrence of this wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + # Let's imagine an occurrence of this wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - # Our wall occurrence needs to have a "set usage" which describes - # how the layers relate to a reference line (typically a 2D line - # representing the extents of the wall). Usages are special since - # they automatically detect the inherited material set from the - # type. You'd write similar code for a profile set. - rel = ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterialLayerSetUsage") + # Our wall occurrence needs to have a "set usage" which describes + # how the layers relate to a reference line (typically a 2D line + # representing the extents of the wall). Usages are special since + # they automatically detect the inherited material set from the + # type. You'd write similar code for a profile set. + rel = ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterialLayerSetUsage") - # Let's change the offset from the reference line to be 200mm - # instead of the default of 0mm. - ifcopenshell.api.run("material.edit_layer_usage", model, - usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200}) - """ - self.file = file - self.settings = {"usage": usage, "attributes": attributes or {}} + # Let's change the offset from the reference line to be 200mm + # instead of the default of 0mm. + ifcopenshell.api.run("material.edit_layer_usage", model, + usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200}) + """ + settings = {"usage": usage, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["usage"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["usage"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py index c712af15ac..87b3f2c7fb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py @@ -17,13 +17,10 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, material=None, attributes=None): - """Edits the attributes of an IfcMaterial""" - - self.file = file - self.settings = {"material": material, "attributes": attributes or {}} +def edit_material(file, material=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterial""" - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["material"], name, value) + settings = {"material": material, "attributes": attributes or {}} + + for name, value in settings["attributes"].items(): + setattr(settings["material"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py index 6fc781f9a6..aa1310dbca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -17,72 +17,69 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, profile=None, attributes=None, profile_def=None, material=None): - """Edits the attributes of an IfcMaterialProfile +def edit_profile(file, profile=None, attributes=None, profile_def=None, material=None) -> None: + """Edits the attributes of an IfcMaterialProfile - For more information about the attributes and data types of an - IfcMaterialProfile, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialProfile, consult the IFC documentation. - :param profile: The IfcMaterialProfile entity you want to edit - :type profile: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param profile_def: The IfcProfileDef entity the profile curve should be - extruded from. - :type profile_def: ifcopenshell.entity_instance, optional - :param material: The IfcMaterial entity you want to change the profile - to be made from. - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param profile: The IfcMaterialProfile entity you want to edit + :type profile: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param profile_def: The IfcProfileDef entity the profile curve should be + extruded from. + :type profile_def: ifcopenshell.entity_instance, optional + :param material: The IfcMaterial entity you want to change the profile + to be made from. + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a material set to store our profiles. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # Let's create a material set to store our profiles. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a couple steel materials. - steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a couple steel materials. + steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create some I-shaped profiles. Notice how we name our profiles based - # on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - hea200 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", - OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, - ) + # Create some I-shaped profiles. Notice how we name our profiles based + # on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + hea200 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", + OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, + ) - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel1, profile=hea100) + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel1, profile=hea100) - # Edit our profile item to use a HEA200 profile instead made out of - # another type of steel. - ifcopenshell.api.run("material.edit_profile", model, - profile=profile_item, profile_def=hea200, material=steel2) - """ - self.file = file - self.settings = { - "profile": profile, - "attributes": attributes or {}, - "profile_def": profile_def, - "material": material, - } + # Edit our profile item to use a HEA200 profile instead made out of + # another type of steel. + ifcopenshell.api.run("material.edit_profile", model, + profile=profile_item, profile_def=hea200, material=steel2) + """ + settings = { + "profile": profile, + "attributes": attributes or {}, + "profile_def": profile_def, + "material": material, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["profile"], name, value) - if self.settings["material"]: - self.settings["profile"].Material = self.settings["material"] - if self.settings["profile_def"]: - self.settings["profile"].Profile = self.settings["profile_def"] + for name, value in settings["attributes"].items(): + setattr(settings["profile"], name, value) + if settings["material"]: + settings["profile"].Material = settings["material"] + if settings["profile_def"]: + settings["profile"].Profile = settings["profile_def"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py index afd9007c7b..8ad5192570 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py @@ -20,79 +20,82 @@ import ifcopenshell.geom import ifcopenshell.util.representation +def edit_profile_usage(file, usage=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterialProfileSetUsage + + This is typically used to change the cardinal point of the profile. + The cardinal point represents whether the profile is extruded along the + center of the axis line, at a corner, at a shear center, at the bottom, + top, etc. + + For more information about the attributes and data types of an + IfcMaterialProfileSetUsage, consult the IFC documentation. + + :param usage: The IfcMaterialProfileSetUsage entity you want to edit + :type usage: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") + + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) + + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + + # Let's create an occurrence of this beam. + beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") + rel = ifcopenshell.api.run("material.assign_material", model, + products=[beam], type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geometry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) + + # Let's change the cardinal point to be the top center of the axis + # line. This is represented by the number "8". Consult the IFC + # documentation for all the numbers you can use. + ifcopenshell.api.run("material.edit_profile_usage", model, + usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8}) + """ + usecase = Usecase() + + usecase.file = file + usecase.settings = {"usage": usage, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, usage=None, attributes=None): - """Edits the attributes of an IfcMaterialProfileSetUsage - - This is typically used to change the cardinal point of the profile. - The cardinal point represents whether the profile is extruded along the - center of the axis line, at a corner, at a shear center, at the bottom, - top, etc. - - For more information about the attributes and data types of an - IfcMaterialProfileSetUsage, consult the IFC documentation. - - :param usage: The IfcMaterialProfileSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") - - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) - - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - - # Let's create an occurrence of this beam. - beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") - rel = ifcopenshell.api.run("material.assign_material", model, - products=[beam], type="IfcMaterialProfileSetUsage") - - # Let's give a 1000mm long beam body representation. - body = ifcopenshell.api.run("geometry.add_profile_representation", - context=body_context, profile=hea100, depth=1000) - ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) - - # Let's change the cardinal point to be the top center of the axis - # line. This is represented by the number "8". Consult the IFC - # documentation for all the numbers you can use. - ifcopenshell.api.run("material.edit_profile_usage", model, - usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8}) - """ - - self.file = file - self.settings = {"usage": usage, "attributes": attributes or {}} - def execute(self): self.cardinal_point = self.settings["attributes"].get("CardinalPoint") if self.cardinal_point and self.cardinal_point != self.settings["usage"].CardinalPoint: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py index 2fb919d10d..02256e0695 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py @@ -17,42 +17,39 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent=None): - """Removes a constituent from a constituent set +def remove_constituent(file, constituent=None) -> None: + """Removes a constituent from a constituent set - Note that it is invalid to have zero items in a set, so you should leave - at least one constituent to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one constituent to ensure a valid IFC dataset. - :param constituent: The IfcMaterialConstituent entity you want to remove - :type constituent: ifcopenshell.entity_instance - :return: None - :rtype: None + :param constituent: The IfcMaterialConstituent entity you want to remove + :type constituent: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set for windows made out of aluminium and glass. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # Create a material set for windows made out of aluminium and glass. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two constituents in our set. - framing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium) - glazing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Now let's use those materials as two constituents in our set. + framing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium) + glazing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Let's remove the glass constituent. Note that we should not remove - # the framing, at this would mean there are no constituents which is - # invalid. - ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing) - """ - self.file = file - self.settings = {"constituent": constituent} + # Let's remove the glass constituent. Note that we should not remove + # the framing, at this would mean there are no constituents which is + # invalid. + ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing) + """ + settings = {"constituent": constituent} - def execute(self): - self.file.remove(self.settings["constituent"]) + file.remove(settings["constituent"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py index f068641533..fda5cf2151 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py @@ -17,45 +17,42 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None): - """Removes a layer from a layer set +def remove_layer(file, layer=None) -> None: + """Removes a layer from a layer set - Note that it is invalid to have zero items in a set, so you should leave - at least one layer to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one layer to ensure a valid IFC dataset. - :param layer: The IfcMaterialLayer entity you want to remove - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param layer: The IfcMaterialLayer entity you want to remove + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set for steel stud partition walls. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # Create a material set for steel stud partition walls. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13}) - layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92}) - layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13}) + layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92}) + layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13}) - # Let's remove the last layer, such that the wall might be clad only - # one one side such as to line a services riser. - ifcopenshell.api.run("material.remove_layer", model, layer=layer3) - """ - self.file = file - self.settings = {"layer": layer} + # Let's remove the last layer, such that the wall might be clad only + # one one side such as to line a services riser. + ifcopenshell.api.run("material.remove_layer", model, layer=layer3) + """ + settings = {"layer": layer} - def execute(self): - self.file.remove(self.settings["layer"]) + file.remove(settings["layer"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py index 276d9e64d2..a41b6ec7a8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -19,44 +19,41 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material_list=None, material_index=0): - """Removes an item in an material list +def remove_list_item(file, material_list=None, material_index=0) -> None: + """Removes an item in an material list - Note that it is invalid to have zero items in a list, so you should leave - at least one item to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a list, so you should leave + at least one item to ensure a valid IFC dataset. - :param material_list: The IfcMaterialList entity you want to remove an - item from. - :type material_list: ifcopenshell.entity_instance - :param material_index: The index of the material you want to remove from - the list. Starts counting at 0. Defaults to 0. - :type material_index: int, optional - :return: None - :rtype: None + :param material_list: The IfcMaterialList entity you want to remove an + item from. + :type material_list: ifcopenshell.entity_instance + :param material_index: The index of the material you want to remove from + the list. Starts counting at 0. Defaults to 0. + :type material_index: int, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material list for aluminium windows. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialMaterialList") + # Create a material list for aluminium windows. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialMaterialList") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Let's remove the glass - ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1) - """ - self.file = file - self.settings = {"material_list": material_list, "material_index": material_index} + # Let's remove the glass + ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1) + """ + settings = {"material_list": material_list, "material_index": material_index} - def execute(self): - materials = list(self.settings["material_list"].Materials) - materials.pop(self.settings["material_index"]) - self.settings["material_list"].Materials = materials + materials = list(settings["material_list"].Materials) + materials.pop(settings["material_index"]) + settings["material_list"].Materials = materials diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py index ffdf9693d8..a59fee6caf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py @@ -20,57 +20,54 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Removes a material +def remove_material(file, material=None) -> None: + """Removes a material - If the material is used in a material set, the corresponding layer, - profile, or constituent is also removed. Note that this may result in a - material set with zero items in it, which is invalid, so the user must - take care of this situation themselves. + If the material is used in a material set, the corresponding layer, + profile, or constituent is also removed. Note that this may result in a + material set with zero items in it, which is invalid, so the user must + take care of this situation themselves. - :param material: The IfcMaterial entity you want to remove - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterial entity you want to remove + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + # Create a material + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - # ... and remove it - ifcopenshell.api.run("material.remove_material", model, material=aluminium) - """ - self.file = file - self.settings = {"material": material} + # ... and remove it + ifcopenshell.api.run("material.remove_material", model, material=aluminium) + """ + settings = {"material": material} - def execute(self): - inverse_elements = self.file.get_inverse(self.settings["material"]) - self.file.remove(self.settings["material"]) - # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set - # This can lead to invalid material sets, but we assume the user will deal with it - for inverse in inverse_elements: - if inverse.is_a("IfcMaterialConstituent"): - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialLayer"): - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialProfile"): - self.file.remove(inverse) - elif inverse.is_a("IfcRelAssociatesMaterial"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcMaterialProperties"): - for prop in inverse.Properties or []: - self.file.remove(prop) - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialDefinitionRepresentation"): - for representation in inverse.Representations: - for item in representation.Items: - self.file.remove(item) - self.file.remove(representation) - self.file.remove(inverse) + inverse_elements = file.get_inverse(settings["material"]) + file.remove(settings["material"]) + # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set + # This can lead to invalid material sets, but we assume the user will deal with it + for inverse in inverse_elements: + if inverse.is_a("IfcMaterialConstituent"): + file.remove(inverse) + elif inverse.is_a("IfcMaterialLayer"): + file.remove(inverse) + elif inverse.is_a("IfcMaterialProfile"): + file.remove(inverse) + elif inverse.is_a("IfcRelAssociatesMaterial"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcMaterialProperties"): + for prop in inverse.Properties or []: + file.remove(prop) + file.remove(inverse) + elif inverse.is_a("IfcMaterialDefinitionRepresentation"): + for representation in inverse.Representations: + for item in representation.Items: + file.remove(item) + file.remove(representation) + file.remove(inverse) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py index 79093789aa..5ede76c1c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py @@ -20,65 +20,62 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Removes a material set +def remove_material_set(file, material=None) -> None: + """Removes a material set - All set items, such as layers, profiles, or constituents will also be - removed. However, the materials and profile curves used by the layers, - profiles and constituents will not be removed. + All set items, such as layers, profiles, or constituents will also be + removed. However, the materials and profile curves used by the layers, + profiles and constituents will not be removed. - :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, - IfcMaterialProfileSet entity you want to remove. - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, + IfcMaterialProfileSet entity you want to remove. + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # Create a material set + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Create some materials - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create some materials + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Add some layers - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + # Add some layers + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - # Completely delete the set and all layers. The gypsum and steel - # material still exist, though. - ifcopenshell.api.run("material.remove_material_set", model, material=material_set) - """ + # Completely delete the set and all layers. The gypsum and steel + # material still exist, though. + ifcopenshell.api.run("material.remove_material_set", model, material=material_set) + """ - self.file = file - self.settings = {"material": material} + settings = {"material": material} - def execute(self): - inverse_elements = self.file.get_inverse(self.settings["material"]) - if self.settings["material"].is_a("IfcMaterialLayerSet"): - set_items = self.settings["material"].MaterialLayers or [] - elif self.settings["material"].is_a("IfcMaterialProfileSet"): - set_items = self.settings["material"].MaterialProfiles or [] - elif self.settings["material"].is_a("IfcMaterialConstituentSet"): - set_items = self.settings["material"].MaterialConstituents or [] - elif self.settings["material"].is_a("IfcMaterialList"): - set_items = [] - for set_item in set_items: - self.file.remove(set_item) - self.file.remove(self.settings["material"]) - for inverse in inverse_elements: - if inverse.is_a("IfcRelAssociatesMaterial"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcMaterialProperties"): - for prop in inverse.Properties or []: - self.file.remove(prop) - self.file.remove(inverse) + inverse_elements = file.get_inverse(settings["material"]) + if settings["material"].is_a("IfcMaterialLayerSet"): + set_items = settings["material"].MaterialLayers or [] + elif settings["material"].is_a("IfcMaterialProfileSet"): + set_items = settings["material"].MaterialProfiles or [] + elif settings["material"].is_a("IfcMaterialConstituentSet"): + set_items = settings["material"].MaterialConstituents or [] + elif settings["material"].is_a("IfcMaterialList"): + set_items = [] + for set_item in set_items: + file.remove(set_item) + file.remove(settings["material"]) + for inverse in inverse_elements: + if inverse.is_a("IfcRelAssociatesMaterial"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcMaterialProperties"): + for prop in inverse.Properties or []: + file.remove(prop) + file.remove(inverse) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py index 9c930866ed..858b6f1289 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py @@ -21,58 +21,55 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, profile=None): - """Removes a profile item from a profile set +def remove_profile(file, profile=None) -> None: + """Removes a profile item from a profile set - Note that it is invalid to have zero items in a set, so you should leave - at least one profile to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one profile to ensure a valid IFC dataset. - :param profile: The IfcMaterialProfile entity you want to remove - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None + :param profile: The IfcMaterialProfile entity you want to remove + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # First, let's create a material set. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # First, let's create a material set. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) - # Define that steel material and cross section as a single profile item. - ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) + # Define that steel material and cross section as a single profile item. + ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) - # Imagine a welded square along the length of the profile. - welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, - profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)]) - weld_profile = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=welded_square) + # Imagine a welded square along the length of the profile. + welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, + profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)]) + weld_profile = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=welded_square) - # Let's remove our welded square. - ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile) - """ + # Let's remove our welded square. + ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile) + """ - self.file = file - self.settings = {"profile": profile} + settings = {"profile": profile} - def execute(self): - subelements = set() - for attribute in self.settings["profile"]: - if isinstance(attribute, ifcopenshell.entity_instance): - subelements.add(attribute) - self.file.remove(self.settings["profile"]) - for subelement in subelements: - ifcopenshell.util.element.remove_deep2(self.file, subelement) + subelements = set() + for attribute in settings["profile"]: + if isinstance(attribute, ifcopenshell.entity_instance): + subelements.add(attribute) + file.remove(settings["profile"]) + for subelement in subelements: + ifcopenshell.util.element.remove_deep2(file, subelement) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py index 442fec8b5e..481050ff63 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py @@ -17,56 +17,53 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, material_set=None, old_index=0, new_index=0): - """Reorders an item in a material set +def reorder_set_item(file, material_set=None, old_index=0, new_index=0) -> None: + """Reorders an item in a material set - In some material sets, the order have meaning, like in a layer set. In - other cases, it is purely for human convenience. + In some material sets, the order have meaning, like in a layer set. In + other cases, it is purely for human convenience. - :param material_set: The IfcMaterialSet which you want to reorder an - item in. - :type material_set: ifcopenshell.entity_instance - :param old_index: The index of the item you want to move. This starts - counting from 0. - :type old_index: int - :param new_index: The index of the new position the item will move to. - This starts counting from 0. - :type new_index: int - :return: None - :rtype: None + :param material_set: The IfcMaterialSet which you want to reorder an + item in. + :type material_set: ifcopenshell.entity_instance + :param old_index: The index of the item you want to move. This starts + counting from 0. + :type old_index: int + :param new_index: The index of the new position the item will move to. + This starts counting from 0. + :type new_index: int + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialList") + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialList") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Switch the order around, this has no meaning for a list, so this - # is just for fun. - ifcopenshell.api.run("material.reorder_set_item", model, - material_set=material_set, old_index=0, new_index=1) - """ - self.file = file - self.settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index} + # Switch the order around, this has no meaning for a list, so this + # is just for fun. + ifcopenshell.api.run("material.reorder_set_item", model, + material_set=material_set, old_index=0, new_index=1) + """ + settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index} - def execute(self): - if self.settings["material_set"].is_a("IfcMaterialConstituentSet"): - set_name = "MaterialConstituents" - elif self.settings["material_set"].is_a("IfcMaterialLayerSet"): - set_name = "MaterialLayers" - elif self.settings["material_set"].is_a("IfcMaterialProfileSet"): - set_name = "MaterialProfiles" - elif self.settings["material_set"].is_a("IfcMaterialList"): - set_name = "Materials" - items = list(getattr(self.settings["material_set"], set_name) or []) - items.insert(self.settings["new_index"], items.pop(self.settings["old_index"])) - setattr(self.settings["material_set"], set_name, items) + if settings["material_set"].is_a("IfcMaterialConstituentSet"): + set_name = "MaterialConstituents" + elif settings["material_set"].is_a("IfcMaterialLayerSet"): + set_name = "MaterialLayers" + elif settings["material_set"].is_a("IfcMaterialProfileSet"): + set_name = "MaterialProfiles" + elif settings["material_set"].is_a("IfcMaterialList"): + set_name = "Materials" + items = list(getattr(settings["material_set"], set_name) or []) + items.insert(settings["new_index"], items.pop(settings["old_index"])) + setattr(settings["material_set"], set_name, items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py index 5f88963c36..93a7f11aea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py @@ -21,41 +21,44 @@ import ifcopenshell.api import ifcopenshell.util.element +def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Removes any material relationship with the list of products + + A product can only have one material assigned to it, which is why it is + not necessary to specify the material to unassign. The material is not + removed, only the relationship is removed. + + If the product does not have a material, nothing happens. + + :param products: The list IfcProducts that may or may not have a material + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None + + Example: + + .. code:: python + + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Let's imagine a concrete bench made out of concrete. + bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + ifcopenshell.api.run("material.assign_material", model, + products=[bench_type], type="IfcMaterial", material=concrete) + + # Let's change our mind and remove the concrete assignment. The + # concrete material still exists, but the bench is no longer made + # out of concrete now. + ifcopenshell.api.run("material.unassign_material", model, products=[bench_type]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"products": products} + return usecase.execute() + + class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Removes any material relationship with the list of products - - A product can only have one material assigned to it, which is why it is - not necessary to specify the material to unassign. The material is not - removed, only the relationship is removed. - - If the product does not have a material, nothing happens. - - :param products: The list IfcProducts that may or may not have a material - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None - - Example: - - .. code:: python - - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Let's imagine a concrete bench made out of concrete. - bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - ifcopenshell.api.run("material.assign_material", model, - products=[bench_type], type="IfcMaterial", material=concrete) - - # Let's change our mind and remove the concrete assignment. The - # concrete material still exists, but the bench is no longer made - # out of concrete now. - ifcopenshell.api.run("material.unassign_material", model, products=[bench_type]) - """ - self.file = file - self.settings = {"products": products} - - def execute(self) -> None: + def execute(self): self.products = set(self.settings["products"]) if not self.products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py index e0caddbe3c..242e12eb11 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_object import assign_object +from .change_nest import change_nest +from .reorder_nesting import reorder_nesting +from .unassign_object import unassign_object diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py index 1c9ab8c9b0..7bf76e5c08 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py @@ -22,156 +22,152 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_objects: list[ifcopenshell.entity_instance], - relating_object: ifcopenshell.entity_instance, - ): - """Assigns objects as nested children to a parent host +def assign_object( + file: ifcopenshell.file, + related_objects: list[ifcopenshell.entity_instance], + relating_object: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns objects as nested children to a parent host - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. - Another type of "decomposition" relationship is known as "nesting". - Nesting is used when an child object is physically attached to a parent - host object, through a physical predetermined connection point. The - child object must be specifically designed to attach to a other objects - at specific positions with a particular form factor. Examples include - faucets which must always be attached through a predrilled hole in a - basin. Alternatively, it could be a modular attachment with a - correlating male and female joint that must join at a particular point. - Because there is a strict connection point, when the parent moves, all - nested children must move with the parent. Another example might be a - predrilled hole in a door panel where hardware must fit through. + Another type of "decomposition" relationship is known as "nesting". + Nesting is used when an child object is physically attached to a parent + host object, through a physical predetermined connection point. The + child object must be specifically designed to attach to a other objects + at specific positions with a particular form factor. Examples include + faucets which must always be attached through a predrilled hole in a + basin. Alternatively, it could be a modular attachment with a + correlating male and female joint that must join at a particular point. + Because there is a strict connection point, when the parent moves, all + nested children must move with the parent. Another example might be a + predrilled hole in a door panel where hardware must fit through. - Nesting relationships are not very commonly used in most design and - construction models. Its main usecase is in modular construction, kit of - parts, or fabrication models. + Nesting relationships are not very commonly used in most design and + construction models. Its main usecase is in modular construction, kit of + parts, or fabrication models. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an nesting relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an nesting relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - IFC placements follow a convention where the placement is relative to - its parent in the spatial hierarchy. If your product has a placement, - its placement will be recalculated to follow this convention. + IFC placements follow a convention where the placement is relative to + its parent in the spatial hierarchy. If your product has a placement, + its placement will be recalculated to follow this convention. - For physical connections which are part of a distribution system, such - as a plug connecting into a GPO, or a duct connecting to an AHU, or two - pipe segments connecting with a bend, tee, or wye fitting, you should - not nest the two objects directly. Instead, you should nest a connection - port, which determines the type of compatible distribution flow that can - be connected to it. To do this, do not use this function, but instead - use the more specific functions in the ifcopenshell.api.system module. + For physical connections which are part of a distribution system, such + as a plug connecting into a GPO, or a duct connecting to an AHU, or two + pipe segments connecting with a bend, tee, or wye fitting, you should + not nest the two objects directly. Instead, you should nest a connection + port, which determines the type of compatible distribution flow that can + be connected to it. To do this, do not use this function, but instead + use the more specific functions in the ifcopenshell.api.system module. - Note that nesting relationships may also be used by non-physical - elements, such as cost items or tasks. In this context, nesting means - that there is an implied order to the child cost items or tasks (i.e. - task 1 should be shown before task 2). It is not necessary to use this - function for nesting non-physical elements. Instead, it is recommended - to instead just use the relevant API functions, like - ifcopenshell.api.cost.add_cost_item or - ifcopenshell.api.sequence.add_task. + Note that nesting relationships may also be used by non-physical + elements, such as cost items or tasks. In this context, nesting means + that there is an implied order to the child cost items or tasks (i.e. + task 1 should be shown before task 2). It is not necessary to use this + function for nesting non-physical elements. Instead, it is recommended + to instead just use the relevant API functions, like + ifcopenshell.api.cost.add_cost_item or + ifcopenshell.api.sequence.add_task. - :param related_objects: The list of children of the nesting relationship, - typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance] - :param relating_object: The host parent of the nesting relationship, - typically an IfcElement. - :type relating_object: ifcopenshell.entity_instance - :return: The IfcRelNests relationship instance - or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param related_objects: The list of children of the nesting relationship, + typically IfcElements. + :type related_objects: list[ifcopenshell.entity_instance] + :param relating_object: The host parent of the nesting relationship, + typically an IfcElement. + :type relating_object: ifcopenshell.entity_instance + :return: The IfcRelNests relationship instance + or `None` if `related_objects` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Faucets are designed to attach onto a sink through a predrilled hole. - sink = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcSanitaryTerminal", predefined_type="SINK") - faucet = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcValve", predefined_type="FAUCET") - ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink) - """ - self.file = file - self.settings = {"related_objects": related_objects, "relating_object": relating_object} + # Faucets are designed to attach onto a sink through a predrilled hole. + sink = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcSanitaryTerminal", predefined_type="SINK") + faucet = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcValve", predefined_type="FAUCET") + ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink) + """ + settings = {"related_objects": related_objects, "relating_object": relating_object} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["related_objects"]: - return + if not settings["related_objects"]: + return - ifc2x3 = self.file.schema == "IFC2X3" + ifc2x3 = file.schema == "IFC2X3" - related_objects = set(self.settings["related_objects"]) - relating_object = self.settings["relating_object"] + related_objects = set(settings["related_objects"]) + relating_object = settings["relating_object"] + if ifc2x3: + is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None) + else: + is_nested_by = next((i for i in relating_object.IsNestedBy), None) + + previous_nests_rels: set[ifcopenshell.entity_instance] = set() + objects_without_nests: list[ifcopenshell.entity_instance] = [] + objects_with_nests: list[ifcopenshell.entity_instance] = [] + + # check if there is anything to change + for object in related_objects: if ifc2x3: - is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None) + object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None) else: - is_nested_by = next((i for i in relating_object.IsNestedBy), None) + object_rel = next(iter(object.Nests), None) - previous_nests_rels: set[ifcopenshell.entity_instance] = set() - objects_without_nests: list[ifcopenshell.entity_instance] = [] - objects_with_nests: list[ifcopenshell.entity_instance] = [] + if object_rel is None: + objects_without_nests.append(object) + continue - # check if there is anything to change - for object in related_objects: - if ifc2x3: - object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None) - else: - object_rel = next(iter(object.Nests), None) + # either is_nested_by is None or product is part of different rel + if object_rel != is_nested_by: + previous_nests_rels.add(object_rel) + objects_with_nests.append(object) - if object_rel is None: - objects_without_nests.append(object) - continue - - # either is_nested_by is None or product is part of different rel - if object_rel != is_nested_by: - previous_nests_rels.add(object_rel) - objects_with_nests.append(object) - - # products with already assigned nestings will be skipped - - objects_to_change = objects_without_nests + objects_with_nests - # nothing to change - if not objects_to_change: - return is_nested_by - - # NOTE: An object can both be nested and assigned to a container or an aggregate. - - # unassign elements from previous nests - for nests in previous_nests_rels: - cur_related_objects = set(nests.RelatedObjects) - related_objects - if cur_related_objects: - nests.RelatedObjects = list(cur_related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests}) - else: - history = nests.OwnerHistory - self.file.remove(nests) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new nesting - if is_nested_by: - is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_nested_by}) - else: - is_nested_by = self.file.create_entity( - "IfcRelNests", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": list(related_objects), - "RelatingObject": relating_object, - } - ) - - # NOTE: Creating a nesting relationship doesn't localize the object's placement, - # unlike assigning it to an aggregate or a container. + # products with already assigned nestings will be skipped + objects_to_change = objects_without_nests + objects_with_nests + # nothing to change + if not objects_to_change: return is_nested_by + + # NOTE: An object can both be nested and assigned to a container or an aggregate. + + # unassign elements from previous nests + for nests in previous_nests_rels: + cur_related_objects = set(nests.RelatedObjects) - related_objects + if cur_related_objects: + nests.RelatedObjects = list(cur_related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests}) + else: + history = nests.OwnerHistory + file.remove(nests) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new nesting + if is_nested_by: + is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_nested_by}) + else: + is_nested_by = file.create_entity( + "IfcRelNests", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": list(related_objects), + "RelatingObject": relating_object, + } + ) + + # NOTE: Creating a nesting relationship doesn't localize the object's placement, + # unlike assigning it to an aggregate or a container. + + return is_nested_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py index 6d83a9e4e8..22ba7d0fc3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py @@ -21,29 +21,26 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, item=None, new_parent=None): - """Assigns a cost item to a new parent cost item""" - self.file = file - self.settings = {"item": item, "new_parent": new_parent} +def change_nest(file, item=None, new_parent=None) -> None: + """Assigns a cost item to a new parent cost item""" + settings = {"item": item, "new_parent": new_parent} - def execute(self): - if not self.settings["item"].Nests: - return - nests = self.settings["item"].Nests[0] - related_objects = list(nests.RelatedObjects) - related_objects.remove(self.settings["item"]) - if related_objects: - nests.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests}) - else: - history = nests.OwnerHistory - self.file.remove(nests) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[self.settings["item"]], - relating_object=self.settings["new_parent"], - ) + if not settings["item"].Nests: + return + nests = settings["item"].Nests[0] + related_objects = list(nests.RelatedObjects) + related_objects.remove(settings["item"]) + if related_objects: + nests.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests}) + else: + history = nests.OwnerHistory + file.remove(nests) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[settings["item"]], + relating_object=settings["new_parent"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py index cdc23c07a5..63b591ee28 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py @@ -17,20 +17,17 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, item=None, old_index=0, new_index=0): - """Reorders an item in a nesting set""" - self.file = file - self.settings = {"item": item, "old_index":old_index, "new_index": new_index} +def reorder_nesting(file, item=None, old_index=0, new_index=0) -> None: + """Reorders an item in a nesting set""" + settings = {"item": item, "old_index": old_index, "new_index": new_index} - def execute(self): - if not self.settings["item"].Nests: - return - nesting_set = self.settings["item"].Nests[0] - if not self.settings["old_index"]: - old_index = nesting_set.RelatedObjects.index(self.settings["item"]) - else: - old_index = self.settings["old_index"] - items = list(getattr(nesting_set, "RelatedObjects") or []) - items.insert(self.settings["new_index"], items.pop(old_index)) - setattr(nesting_set, "RelatedObjects", items) + if not settings["item"].Nests: + return + nesting_set = settings["item"].Nests[0] + if not settings["old_index"]: + old_index = nesting_set.RelatedObjects.index(settings["item"]) + else: + old_index = settings["old_index"] + items = list(getattr(nesting_set, "RelatedObjects") or []) + items.insert(settings["new_index"], items.pop(old_index)) + setattr(nesting_set, "RelatedObjects", items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py index b9f48b4b5b..42e35777ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py @@ -21,57 +21,54 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]): - """Unassigns related_objects from their nests. +def unassign_object(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None: + """Unassigns related_objects from their nests. - An object (the whole within a decomposition) is Nested by zero or one more smaller objects. - This function will remove this nesting relationship. + An object (the whole within a decomposition) is Nested by zero or one more smaller objects. + This function will remove this nesting relationship. - If the object is not part of a nesting relationship, nothing will happen. + If the object is not part of a nesting relationship, nothing will happen. - :param related_objects: The list of children of the nesting relationship, - typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param related_objects: The list of children of the nesting relationship, + typically IfcElements. + :type related_objects: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks") - subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") - subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") - ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task) - ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task) - # nothing is returned - rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1]) - # nothing is returned, relationship is removed - ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2]) - """ - self.file = file - self.settings = {"related_objects": related_objects} + task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks") + subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") + subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") + ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task) + ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task) + # nothing is returned + rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1]) + # nothing is returned, relationship is removed + ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2]) + """ + settings = {"related_objects": related_objects} - def execute(self) -> None: - related_objects = set(self.settings["related_objects"]) - ifc2x3 = self.file.schema == "IFC2X3" - if ifc2x3: - rels = set( - rel - for object in related_objects - if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None)) - ) + related_objects = set(settings["related_objects"]) + ifc2x3 = file.schema == "IFC2X3" + if ifc2x3: + rels = set( + rel + for object in related_objects + if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None)) + ) + else: + rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None))) + + for rel in rels: + related_objects = set(rel.RelatedObjects) - related_objects + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None))) - - for rel in rels: - related_objects = set(rel.RelatedObjects) - related_objects - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py index e0caddbe3c..755fa4fc5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py @@ -15,3 +15,27 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_actor import add_actor +from .add_address import add_address +from .add_application import add_application +from .add_organisation import add_organisation +from .add_person import add_person +from .add_person_and_organisation import add_person_and_organisation +from .add_role import add_role +from .assign_actor import assign_actor +from .create_owner_history import create_owner_history +from .edit_actor import edit_actor +from .edit_address import edit_address +from .edit_organisation import edit_organisation +from .edit_person import edit_person +from .edit_role import edit_role +from .remove_actor import remove_actor +from .remove_address import remove_address +from .remove_application import remove_application +from .remove_organisation import remove_organisation +from .remove_person import remove_person +from .remove_person_and_organisation import remove_person_and_organisation +from .remove_role import remove_role +from .unassign_actor import unassign_actor +from .update_owner_history import update_owner_history diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py index c3ff65c3d1..124561f136 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py @@ -21,49 +21,46 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, actor=None, ifc_class="IfcActor"): - """Adds a new actor +def add_actor(file, actor=None, ifc_class="IfcActor") -> None: + """Adds a new actor - An actor is a person or an organisation who has a responsibility or role - to play in a project. Actor roles include design consultants, - architects, engineers, cost planners, suppliers, manufacturers, - warrantors, owners, subcontractors, etc. + An actor is a person or an organisation who has a responsibility or role + to play in a project. Actor roles include design consultants, + architects, engineers, cost planners, suppliers, manufacturers, + warrantors, owners, subcontractors, etc. - Actors may either be project actors, who are responsible for the - delivery of the project, or occupants, who are responsible for the - consumption of the project. + Actors may either be project actors, who are responsible for the + delivery of the project, or occupants, who are responsible for the + consumption of the project. - Identifying and managing actors is critical for asset management, and - identifying liability for legal submissions. + Identifying and managing actors is critical for asset management, and + identifying liability for legal submissions. - :param actor: Most commonly, an IfcOrganization (in compliance with GDPR - requirements for non personally identifiable information), or an - IfcPerson if it is a sole individual, or an IfcPersonAndOrganization - if a specific person is liable within an organisation and must be - legally nominated. - :type actor: ifcopenshell.entity_instance - :param ifc_class: Either "IfcActor" or "IfcOccupant". - :type ifc_class: str, optional - :return: The newly created IfcActor or IfcOccupant - :rtype: ifcopenshell.entity_instance + :param actor: Most commonly, an IfcOrganization (in compliance with GDPR + requirements for non personally identifiable information), or an + IfcPerson if it is a sole individual, or an IfcPersonAndOrganization + if a specific person is liable within an organisation and must be + legally nominated. + :type actor: ifcopenshell.entity_instance + :param ifc_class: Either "IfcActor" or "IfcOccupant". + :type ifc_class: str, optional + :return: The newly created IfcActor or IfcOccupant + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - """ - self.file = file - self.settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"} + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + """ + settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"} - def execute(self): - actor = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=self.settings["ifc_class"]) - actor.TheActor = self.settings["actor"] - return actor + actor = ifcopenshell.api.run("root.create_entity", file, ifc_class=settings["ifc_class"]) + actor.TheActor = settings["actor"] + return actor diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py index b184408fa9..a214c86d81 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py @@ -17,58 +17,53 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, assigned_object=None, ifc_class="IfcPostalAddress"): - """Add a new telecom or postal address to an organisation or person +def add_address(file, assigned_object=None, ifc_class="IfcPostalAddress") -> None: + """Add a new telecom or postal address to an organisation or person - A person or organisation may have associated contact details such as - phone numbers, mailing addresses, websites, email addresses, and instant - messaging handles. This information is critical in recording the contact - information of manufacturers and suppliers for facility management, or - liable actors. + A person or organisation may have associated contact details such as + phone numbers, mailing addresses, websites, email addresses, and instant + messaging handles. This information is critical in recording the contact + information of manufacturers and suppliers for facility management, or + liable actors. - There are two types of addresses, postal addresses for physical snail - mail, and telecom addresses for telephone or internet contact numbers - and addresses. + There are two types of addresses, postal addresses for physical snail + mail, and telecom addresses for telephone or internet contact numbers + and addresses. - :param assigned_object: The IfcOrganization or IfcPerson the contact - address belongs to. - :type assigned_object: ifcopenshell.entity_instance - :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults - to IfcPostalAddress. - :type ifc_class: str, optional - :return: The new IfcPostalAddress or IfcTelecomAddress - :rtype: ifcopenshell.entity_instance + :param assigned_object: The IfcOrganization or IfcPerson the contact + address belongs to. + :type assigned_object: ifcopenshell.entity_instance + :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults + to IfcPostalAddress. + :type ifc_class: str, optional + :return: The new IfcPostalAddress or IfcTelecomAddress + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model) + organisation = ifcopenshell.api.run("owner.add_organisation", model) - # A snail mail address - postal = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") - ifcopenshell.api.run("owner.edit_address", model, address=postal, - attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], - "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) + # A snail mail address + postal = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") + ifcopenshell.api.run("owner.edit_address", model, address=postal, + attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], + "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) - # A phone or internet address - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["bobthebuilder@example.com"], - "WWWHomePageURL": "https://thinkmoult.com"}) - """ - self.file = file - self.settings = {"assigned_object": assigned_object, "ifc_class": ifc_class} + # A phone or internet address + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["bobthebuilder@example.com"], + "WWWHomePageURL": "https://thinkmoult.com"}) + """ + settings = {"assigned_object": assigned_object, "ifc_class": ifc_class} - def execute(self): - address = self.file.create_entity(self.settings["ifc_class"], "OFFICE") - addresses = ( - list(self.settings["assigned_object"].Addresses) if self.settings["assigned_object"].Addresses else [] - ) - addresses.append(address) - self.settings["assigned_object"].Addresses = addresses - return address + address = file.create_entity(settings["ifc_class"], "OFFICE") + addresses = list(settings["assigned_object"].Addresses) if settings["assigned_object"].Addresses else [] + addresses.append(address) + settings["assigned_object"].Addresses = addresses + return address diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py index 92861a3417..07a59db0d7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py @@ -19,50 +19,52 @@ import ifcopenshell.api +def add_application( + file, + application_developer=None, + version=None, + application_full_name="IfcOpenShell", + application_identifier="IfcOpenShell", +) -> None: + """Adds a new application + + IFC data may be associated with an authoring application to identify + which application was responsible for editing or authoring the data. An + application is defined by the developing organisation, as well as a full + name and identifier. This is akin to how web browsers have an + identification string. + + :param application_developer: The IfcOrganization responsible for + creating the application. Defaults to generating an IfcOpenShell + organisation if none is provided. + :type application_developer: ifcopenshell.entity_instance, optional + :param version: The version of the application. Defaults to the + ifcopenshell.version data if not specified. + :type version: str, optional + :param application_full_name: The name of the application + :type application_full_name: str, optional + :param application_identifier: An identification string for the + application intended for computers to read. + :type application_identifier: str, optional + + Example: + + .. code:: python + + application = ifcopenshell.api.run("owner.add_application", model) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "application_developer": application_developer, + "version": version or ifcopenshell.version, + "application_full_name": application_full_name, + "application_identifier": application_identifier, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - application_developer=None, - version=None, - application_full_name="IfcOpenShell", - application_identifier="IfcOpenShell", - ): - """Adds a new application - - IFC data may be associated with an authoring application to identify - which application was responsible for editing or authoring the data. An - application is defined by the developing organisation, as well as a full - name and identifier. This is akin to how web browsers have an - identification string. - - :param application_developer: The IfcOrganization responsible for - creating the application. Defaults to generating an IfcOpenShell - organisation if none is provided. - :type application_developer: ifcopenshell.entity_instance, optional - :param version: The version of the application. Defaults to the - ifcopenshell.version data if not specified. - :type version: str, optional - :param application_full_name: The name of the application - :type application_full_name: str, optional - :param application_identifier: An identification string for the - application intended for computers to read. - :type application_identifier: str, optional - - Example: - - .. code:: python - - application = ifcopenshell.api.run("owner.add_application", model) - """ - self.file = file - self.settings = { - "application_developer": application_developer, - "version": version or ifcopenshell.version, - "application_full_name": application_full_name, - "application_identifier": application_identifier, - } - def execute(self): if not self.settings["application_developer"]: self.settings["application_developer"] = self.create_application_organisation() diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py index 2127acd570..354d66f76a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py @@ -18,38 +18,37 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science"): - """Adds a new organisation +def add_organisation( + file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science" +) -> ifcopenshell.entity_instance: + """Adds a new organisation - Organisations are the main way to identify manufacturers, suppliers, and - other actors who do not have a single representative or must not have - any personally identifiable information. + Organisations are the main way to identify manufacturers, suppliers, and + other actors who do not have a single representative or must not have + any personally identifiable information. - :param identification: The short code identifying the organisation. - Sometimes used in drawing naming schemes. Otherise used as a - canonicalised way of computers to identify the organisation. Like - their stock name. - :type identification: str, optional - :param name: The legal name of the organisation - :type name: str, optional - :return: The newly created IfcOrganization - :rtype: ifcopenshell.entity_instance + :param identification: The short code identifying the organisation. + Sometimes used in drawing naming schemes. Otherise used as a + canonicalised way of computers to identify the organisation. Like + their stock name. + :type identification: str, optional + :param name: The legal name of the organisation + :type name: str, optional + :return: The newly created IfcOrganization + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - """ - self.file = file - self.settings = {"identification": identification, "name": name} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + """ + settings = {"identification": identification, "name": name} - def execute(self) -> ifcopenshell.entity_instance: - data = {"Name": self.settings["name"]} - if self.file.schema == "IFC2X3": - data["Id"] = self.settings["identification"] - else: - data["Identification"] = self.settings["identification"] - return self.file.create_entity("IfcOrganization", **data) + data = {"Name": settings["name"]} + if file.schema == "IFC2X3": + data["Id"] = settings["identification"] + else: + data["Identification"] = settings["identification"] + return file.create_entity("IfcOrganization", **data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py index a607d8af29..5d571920d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py @@ -18,47 +18,43 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - identification: str = "HSeldon", - family_name: str = "Seldon", - given_name: str = "Hari", - ): - """Adds a new person +def add_person( + file: ifcopenshell.entity_instance, + identification: str = "HSeldon", + family_name: str = "Seldon", + given_name: str = "Hari", +) -> None: + """Adds a new person - Persons are used to identify a legal or liable representative of an - organisation or point of contact. + Persons are used to identify a legal or liable representative of an + organisation or point of contact. - :param identification: The computer readable unique identification of - the person. For example, their username in a CDE or alias. - :type identification: str, optional - :param family_name: The family name - :type family_name: str, optional - :param given_name: The given name - :type given_name: str, optional - :return: The newly created IfcPerson - :rtype: ifcopenshell.entity_instance + :param identification: The computer readable unique identification of + the person. For example, their username in a CDE or alias. + :type identification: str, optional + :param family_name: The family name + :type family_name: str, optional + :param given_name: The given name + :type given_name: str, optional + :return: The newly created IfcPerson + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - """ - self.file = file - self.settings = { - "identification": identification, - "family_name": family_name, - "given_name": given_name, - } + ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + """ + settings = { + "identification": identification, + "family_name": family_name, + "given_name": given_name, + } - def execute(self) ->ifcopenshell.entity_instance: - data = {"FamilyName": self.settings["family_name"], "GivenName": self.settings["given_name"]} - if self.file.schema == "IFC2X3": - data["Id"] = self.settings["identification"] - else: - data["Identification"] = self.settings["identification"] - return self.file.create_entity("IfcPerson", **data) + data = {"FamilyName": settings["family_name"], "GivenName": settings["given_name"]} + if file.schema == "IFC2X3": + data["Id"] = settings["identification"] + else: + data["Identification"] = settings["identification"] + return file.create_entity("IfcPerson", **data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py index a5987b4b76..7f07c1991c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py @@ -18,40 +18,36 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - person: ifcopenshell.entity_instance, - organisation: ifcopenshell.entity_instance, - ): - """Adds a paired person and organisation +def add_person_and_organisation( + file: ifcopenshell.entity_instance, + person: ifcopenshell.entity_instance, + organisation: ifcopenshell.entity_instance, +) -> ifcopenshell.entity_instance: + """Adds a paired person and organisation - A person and an organisation may be paired to create a representative - belonging to a company. + A person and an organisation may be paired to create a representative + belonging to a company. - :param person: The IfcPerson being the representative of the - organisation. - :type person: ifcopenshell.entity_instance - :param organisation: The IfcOrganization itself. - :type organisation: ifcopenshell.entity_instance - :return: The newly created IfcPersonAndOrganization - :rtype: ifcopenshell.entity_instance + :param person: The IfcPerson being the representative of the + organisation. + :type person: ifcopenshell.entity_instance + :param organisation: The IfcOrganization it + :type organisation: ifcopenshell.entity_instance + :return: The newly created IfcPersonAndOrganization + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") + person = ifcopenshell.api.run("owner.add_person", model, + identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) - """ - self.file = file - self.settings = {"person": person, "organisation": organisation} + ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) + """ + settings = {"person": person, "organisation": organisation} - def execute(self) -> ifcopenshell.entity_instance: - return self.file.createIfcPersonAndOrganization(self.settings["person"], self.settings["organisation"]) + return file.createIfcPersonAndOrganization(settings["person"], settings["organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py index 3b1369877c..415ed4efde 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py @@ -17,47 +17,44 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, assigned_object=None, role="ARCHITECT"): - """Adds and assigns a new role +def add_role(file, assigned_object=None, role="ARCHITECT") -> None: + """Adds and assigns a new role - People and organisations must play one or more roles on a project. Roles - include architects, engineers, subcontractors, clients, manufacturers, - etc. Typically these roles and their corresponding responsibilities will - be outlined in contractual documents. + People and organisations must play one or more roles on a project. Roles + include architects, engineers, subcontractors, clients, manufacturers, + etc. Typically these roles and their corresponding responsibilities will + be outlined in contractual documents. - This function will both add and assign the role to the person or - organisation. + This function will both add and assign the role to the person or + organisation. - :param assigned_object: The IfcPerson or IfcOrganization the role should - be assigned to. - :type assigned_object: ifcopenshell.entity_instance - :param role: The type of role, taken from the IFC documentation for - IfcActorRole, or a custom name. - :type role: str, optional - :return: The newly created IfcActorRole - :rtype: ifcopenshell.entity_instance + :param assigned_object: The IfcPerson or IfcOrganization the role should + be assigned to. + :type assigned_object: ifcopenshell.entity_instance + :param role: The type of role, taken from the IFC documentation for + IfcActorRole, or a custom name. + :type role: str, optional + :return: The newly created IfcActorRole + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - """ - self.file = file - self.settings = {"assigned_object": assigned_object, "role": role} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + """ + settings = {"assigned_object": assigned_object, "role": role} - def execute(self): - element = self.file.createIfcActorRole("ARCHITECT") - if self.settings["role"]: - try: - element.Role = self.settings["role"] - except: - element.Role = "USERDEFINED" - element.UserDefinedRole = self.settings["role"] - roles = list(self.settings["assigned_object"].Roles) if self.settings["assigned_object"].Roles else [] - roles.append(element) - self.settings["assigned_object"].Roles = roles - return element + element = file.createIfcActorRole("ARCHITECT") + if settings["role"]: + try: + element.Role = settings["role"] + except: + element.Role = "USERDEFINED" + element.UserDefinedRole = settings["role"] + roles = list(settings["assigned_object"].Roles) if settings["assigned_object"].Roles else [] + roles.append(element) + settings["assigned_object"].Roles = roles + return element diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py index 1085adeb34..361c32bf2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py @@ -20,88 +20,85 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_actor=None, related_object=None): - """Assigns an actor to an object +def assign_actor(file, relating_actor=None, related_object=None) -> None: + """Assigns an actor to an object - An actor may be assigned to objects which implies that the actor is - responsible for. This is most commonly used in facility management for - indicating the manufacturers, suppliers, and warrantors for product - types. + An actor may be assigned to objects which implies that the actor is + responsible for. This is most commonly used in facility management for + indicating the manufacturers, suppliers, and warrantors for product + types. - Here are a list of objects you may assign an actor to: + Here are a list of objects you may assign an actor to: - * IfcControl: Indicates project directives issued by the actor. - * IfcGroup: Indicates groups for which the actor is responsible. - * IfcProduct: Indicates products for which the actor is responsible. - * IfcProcess: Indicates processes for which the actor is responsible. - * IfcResource: Indicates resources for which the actor is responsible to - allocate, manage, or delegate. This is not the actor actually using - the resource or performing the work. For that type of actor, see - ifcopenshell.api.resource.assign_resource. + * IfcControl: Indicates project directives issued by the actor. + * IfcGroup: Indicates groups for which the actor is responsible. + * IfcProduct: Indicates products for which the actor is responsible. + * IfcProcess: Indicates processes for which the actor is responsible. + * IfcResource: Indicates resources for which the actor is responsible to + allocate, manage, or delegate. This is not the actor actually using + the resource or performing the work. For that type of actor, see + ifcopenshell.api.resource.assign_resource. - :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance - :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToActor relationship. - :rtype: ifcopenshell.entity_instance + :param relating_actor: The IfcActor who is responsible for the object. + :type relating_actor: ifcopenshell.entity_instance + :param related_object: The object the actor is responsible for. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToActor relationship. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We need to procure and install 2 of this particular pump type in our facility. - pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") + # We need to procure and install 2 of this particular pump type in our facility. + pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") - # Define who the manufacturer is - manufacturer = ifcopenshell.api.run("owner.add_organisation", model, - identification="PWP", name="Pumps With Power") - ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") + # Define who the manufacturer is + manufacturer = ifcopenshell.api.run("owner.add_organisation", model, + identification="PWP", name="Pumps With Power") + ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") - # To help our facility manager, it's nice to provide contact details - # of the manufacturer so they know how to call when the pump breaks. - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["contact@example.com"], - "WWWHomePageURL": "https://example.com"}) + # To help our facility manager, it's nice to provide contact details + # of the manufacturer so they know how to call when the pump breaks. + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["contact@example.com"], + "WWWHomePageURL": "https://example.com"}) - # Make the manufacturer responsible for that pump type. - ifcopenshell.api.run("owner.assign_actor", model, - relating_actor=manufacturer, related_object=pump_type) - """ - self.file = file - self.settings = { - "relating_actor": relating_actor, - "related_object": related_object, - } + # Make the manufacturer responsible for that pump type. + ifcopenshell.api.run("owner.assign_actor", model, + relating_actor=manufacturer, related_object=pump_type) + """ + settings = { + "relating_actor": relating_actor, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == self.settings["relating_actor"]: - return + if settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]: + return - rel = None + rel = None - if self.settings["relating_actor"].IsActingUpon: - rel = self.settings["relating_actor"].IsActingUpon[0] + if settings["relating_actor"].IsActingUpon: + rel = settings["relating_actor"].IsActingUpon[0] - if rel: - related_objects = list(rel.RelatedObjects) - related_objects.append(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - rel = self.file.create_entity( - "IfcRelAssignsToActor", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingActor": self.settings["relating_actor"], - } - ) - return rel + if rel: + related_objects = list(rel.RelatedObjects) + related_objects.append(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + rel = file.create_entity( + "IfcRelAssignsToActor", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingActor": settings["relating_actor"], + } + ) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py index 94183e0c6b..2b4729897f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py @@ -22,100 +22,97 @@ import ifcopenshell.api.owner.settings from typing import Union -class Usecase: - def __init__(self, file: ifcopenshell.entity_instance): - """Creates a new owner history indicating an element was added +def create_owner_history(file: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: + """Creates a new owner history indicating an element was added - Any object in IFC with a unique ID and name (such as physical products, - tasks, calendars, etc) may have an owner associated with it. An owner is - a liable person and/or organisation which a bit of metadata indicating - whether they have created the object, edited the object, when the change - was made, and which application they used. + Any object in IFC with a unique ID and name (such as physical products, + tasks, calendars, etc) may have an owner associated with it. An owner is + a liable person and/or organisation which a bit of metadata indicating + whether they have created the object, edited the object, when the change + was made, and which application they used. - IFC does not offer a comprehensive specification for version control and - change tracking, as this is completely out of scope. However this - similar ability allows IFC to satisfy legal requirements where object - ownership, responsibilities, and permissions must be specified. - Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is - not recommended to store this ownership data in IFC4 unless a legal - requirement is in place. + IFC does not offer a comprehensive specification for version control and + change tracking, as this is completely out of scope. However this + similar ability allows IFC to satisfy legal requirements where object + ownership, responsibilities, and permissions must be specified. + Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is + not recommended to store this ownership data in IFC4 unless a legal + requirement is in place. - Because owner tracking is mandatory in IFC2X3, be aware that some - configuration may be required to work correctly. Read on. + Because owner tracking is mandatory in IFC2X3, be aware that some + configuration may be required to work correctly. Read on. - To track the owner, at a minimum we have to know the application that - the element was authored from, as well as the user (person and - organisation) that made the change. The IfcOpenShell API is a low level - software library and will not know what application the API is being - called from, and nor does it have the responsibility to manage the - "active user" making edits, which may be as simple as hardcoding it to - "Bob" or even be as complex as integration with a CDE's authentication - system. As a result, the developer responsible to integrate with - IfcOpenShell is expected to overload the - ifcopenshell.api.owner.settings.get_user and - ifcopenshell.api.owner.settings.get_application functions. + To track the owner, at a minimum we have to know the application that + the element was authored from, as well as the user (person and + organisation) that made the change. The IfcOpenShell API is a low level + software library and will not know what application the API is being + called from, and nor does it have the responsibility to manage the + "active user" making edits, which may be as simple as hardcoding it to + "Bob" or even be as complex as integration with a CDE's authentication + system. As a result, the developer responsible to integrate with + IfcOpenShell is expected to overload the + ifcopenshell.api.owner.settings.get_user and + ifcopenshell.api.owner.settings.get_application functions. - It is not necessary to call this function directly if you are already - using other API calls. It is a low level function only available if you - are writing your own advanced scripts and want to take advantage of the - easier ownership tracking. + It is not necessary to call this function directly if you are already + using other API calls. It is a low level function only available if you + are writing your own advanced scripts and want to take advantage of the + easier ownership tracking. - :return: The newly created IfcOwnerHistory element or `None` if it's - not IFC2X3 and user or application is not found in the current project. - :rtype: Union[ifcopenshell.entity_instance, None] + :return: The newly created IfcOwnerHistory element or `None` if it's + not IFC2X3 and user or application is not found in the current project. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we're writing a small script, not large enough to be - # its own fully branded application. In this case, let's use the - # default application which is prepopulated with "IfcOpenShell" as - # the name and version. - application = ifcopenshell.api.run("owner.add_application", model) + # Let's imagine we're writing a small script, not large enough to be + # its own fully branded application. In this case, let's use the + # default application which is prepopulated with "IfcOpenShell" as + # the name and version. + application = ifcopenshell.api.run("owner.add_application", model) - # Let's imagine we run this as an automated QA process in an - # architectural firm. However, the results must be signed off by the - # registered architect who is liable for the project. - person = ifcopenshell.api.run("owner.add_person", model, - identification="LPARTEE", family_name="Partee", given_name="Leeable") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - user = ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) + # Let's imagine we run this as an automated QA process in an + # architectural firm. However, the results must be signed off by the + # registered architect who is liable for the project. + person = ifcopenshell.api.run("owner.add_person", model, + identification="LPARTEE", family_name="Partee", given_name="Leeable") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + user = ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) - # Let's configure our owner settings to hardcode always returning - # the application and user. In theory, you could build complex user - # access control lookup functions here, but this is simple enough. - ifcopenshell.api.owner.settings.get_user = lambda x: user - ifcopenshell.api.owner.settings.get_application = lambda x: application + # Let's configure our owner settings to hardcode always returning + # the application and user. In theory, you could build complex user + # access control lookup functions here, but this is simple enough. + ifcopenshell.api.owner.settings.get_user = lambda x: user + ifcopenshell.api.owner.settings.get_application = lambda x: application - # We've finished our ownership setup. Now let's start our script and - # create a space. Notice we don't actually call - # create_owner_history at all. This is already automatically handled - # by the API when necessary. Under the hood, the API is actually - # running this code on the IfcSpace element: - # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - """ - self.file = file - self.settings = {} + # We've finished our ownership setup. Now let's start our script and + # create a space. Notice we don't actually call + # create_owner_history at all. This is already automatically handled + # by the API when necessary. Under the hood, the API is actually + # running this code on the IfcSpace element: + # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + """ + settings = {} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - user = ifcopenshell.api.owner.settings.get_user(self.file) - if self.file.schema != "IFC2X3" and not user: - return - application = ifcopenshell.api.owner.settings.get_application(self.file) - if self.file.schema != "IFC2X3" and not application: - return - return self.file.create_entity( - "IfcOwnerHistory", - OwningUser=user, - OwningApplication=application, - State="READWRITE", - ChangeAction="ADDED", - LastModifiedDate=int(time.time()), - LastModifyingUser=user, - LastModifyingApplication=application, - CreationDate=int(time.time()), - ) + user = ifcopenshell.api.owner.settings.get_user(file) + if file.schema != "IFC2X3" and not user: + return + application = ifcopenshell.api.owner.settings.get_application(file) + if file.schema != "IFC2X3" and not application: + return + return file.create_entity( + "IfcOwnerHistory", + OwningUser=user, + OwningApplication=application, + State="READWRITE", + ChangeAction="ADDED", + LastModifiedDate=int(time.time()), + LastModifyingUser=user, + LastModifyingApplication=application, + CreationDate=int(time.time()), + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py index eb6491b359..e1125ab212 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, actor=None, attributes=None): - """Edits the attributes of an IfcActor +def edit_actor(file, actor=None, attributes=None) -> None: + """Edits the attributes of an IfcActor - For more information about the attributes and data types of an - IfcActor, consult the IFC documentation. + For more information about the attributes and data types of an + IfcActor, consult the IFC documentation. - :param actor: The IfcActor entity you want to edit - :type actor: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param actor: The IfcActor entity you want to edit + :type actor: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # Edit the description of the attribute. - ifcopenshell.api.run("actor.edit_actor", model, - actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."}) - """ - self.file = file - self.settings = {"actor": actor, "attributes": attributes or {}} + # Edit the description of the attribute. + ifcopenshell.api.run("actor.edit_actor", model, + actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."}) + """ + settings = {"actor": actor, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["actor"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["actor"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py index 0f48af25d5..ba74f0ede6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py @@ -17,42 +17,39 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, address=None, attributes=None): - """Edits the attributes of an IfcAddress +def edit_address(file, address=None, attributes=None) -> None: + """Edits the attributes of an IfcAddress - For more information about the attributes and data types of an - IfcAddress, consult the IFC documentation. + For more information about the attributes and data types of an + IfcAddress, consult the IFC documentation. - :param address: The IfcAddress entity you want to edit - :type address: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param address: The IfcAddress entity you want to edit + :type address: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A snail mail address - postal = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") - ifcopenshell.api.run("owner.edit_address", model, address=postal, - attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], - "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) + # A snail mail address + postal = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") + ifcopenshell.api.run("owner.edit_address", model, address=postal, + attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], + "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) - # A phone or internet address - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["bobthebuilder@example.com"], - "WWWHomePageURL": "https://thinkmoult.com"}) - """ - self.file = file - self.settings = {"address": address, "attributes": attributes or {}} + # A phone or internet address + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["bobthebuilder@example.com"], + "WWWHomePageURL": "https://thinkmoult.com"}) + """ + settings = {"address": address, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["address"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["address"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py index 19c8d1e431..012e9152ba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, organisation=None, attributes=None): - """Edits the attributes of an IfcOrganization +def edit_organisation(file, organisation=None, attributes=None) -> None: + """Edits the attributes of an IfcOrganization - For more information about the attributes and data types of an - IfcOrganization, consult the IFC documentation. + For more information about the attributes and data types of an + IfcOrganization, consult the IFC documentation. - :param organisation: The IfcOrganization entity you want to edit - :type organisation: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param organisation: The IfcOrganization entity you want to edit + :type organisation: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects With Ballpens") - ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation, - attributes={"name": "Architects Without Ballpens"}) - """ - self.file = file - self.settings = {"organisation": organisation, "attributes": attributes or {}} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects With Ballpens") + ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation, + attributes={"name": "Architects Without Ballpens"}) + """ + settings = {"organisation": organisation, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["organisation"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["organisation"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py index 19eedc23db..a8fdb56168 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, person=None, attributes=None): - """Edits the attributes of an IfcPerson +def edit_person(file, person=None, attributes=None) -> None: + """Edits the attributes of an IfcPerson - For more information about the attributes and data types of an - IfcPerson, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPerson, consult the IFC documentation. - :param person: The IfcPerson entity you want to edit - :type person: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param person: The IfcPerson entity you want to edit + :type person: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - ifcopenshell.api.run("owner.edit_person", model, person=person, - attributes={"MiddleNames": ["The"], "FamilyName": "Builder"}) - """ - self.file = file - self.settings = {"person": person, "attributes": attributes or {}} + person = ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + ifcopenshell.api.run("owner.edit_person", model, person=person, + attributes={"MiddleNames": ["The"], "FamilyName": "Builder"}) + """ + settings = {"person": person, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["person"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["person"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py index 160f6f6d91..6934af27e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py @@ -17,36 +17,33 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, role=None, attributes=None): - """Edits the attributes of an IfcActorRole +def edit_role(file, role=None, attributes=None) -> None: + """Edits the attributes of an IfcActorRole - For more information about the attributes and data types of an - IfcActorRole, consult the IFC documentation. + For more information about the attributes and data types of an + IfcActorRole, consult the IFC documentation. - :param role: The IfcActorRole entity you want to edit - :type role: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param role: The IfcActorRole entity you want to edit + :type role: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + person = ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - # By default, the role is an architect - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person) + # By default, the role is an architect + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person) - # But Bob is not an architect - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"}) - """ - self.file = file - self.settings = {"role": role, "attributes": attributes or {}} + # But Bob is not an architect + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"}) + """ + settings = {"role": role, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["role"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["role"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py index ac99299a6f..49feb54179 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, actor=None): - """Removes an actor +def remove_actor(file, actor=None) -> None: + """Removes an actor - :param actor: The IfcActor to remove. - :type actor: ifcopenshell.entity_instance - :return: None - :rtype: None + :param actor: The IfcActor to remove. + :type actor: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # Actually we need ballpens on this project - ifcopenshell.api.run("owner.remove_actor", model, actor=actor) - """ - self.file = file - self.settings = {"actor": actor} + # Actually we need ballpens on this project + ifcopenshell.api.run("owner.remove_actor", model, actor=actor) + """ + settings = {"actor": actor} - def execute(self): - history = self.settings["actor"].OwnerHistory - self.file.remove(self.settings["actor"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["actor"].OwnerHistory + file.remove(settings["actor"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py index 728ffb45f5..5fba84583b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py @@ -17,35 +17,32 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, address=None): - """Removes an address +def remove_address(file, address=None) -> None: + """Removes an address - Naturally, any organisations or people using that address will have the - relationship removed. + Naturally, any organisations or people using that address will have the + relationship removed. - :param address: The IfcAddress to remove. - :type address: ifcopenshell.entity_instance - :return: None - :rtype: None + :param address: The IfcAddress to remove. + :type address: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model) - address = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") + organisation = ifcopenshell.api.run("owner.add_organisation", model) + address = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") - # Change our mind and delete it - ifcopenshell.api.run("owner.remove_address", model, address=address) - """ - self.file = file - self.settings = {"address": address} + # Change our mind and delete it + ifcopenshell.api.run("owner.remove_address", model, address=address) + """ + settings = {"address": address} - def execute(self): - for inverse in self.file.get_inverse(self.settings["address"]): - if inverse.is_a() in ("IfcOrganization", "IfcPerson"): - if inverse.Addresses == (self.settings["address"],): - inverse.Addresses = None - self.file.remove(self.settings["address"]) + for inverse in file.get_inverse(settings["address"]): + if inverse.is_a() in ("IfcOrganization", "IfcPerson"): + if inverse.Addresses == (settings["address"],): + inverse.Addresses = None + file.remove(settings["address"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py index 7e21c07943..16973cfcc4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py @@ -17,27 +17,24 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, application=None): - """Removes an application +def remove_application(file, application=None) -> None: + """Removes an application - Warning: removing an application may invalidate ownership histories. - Check whether or not the application is used anywhere prior to removal. + Warning: removing an application may invalidate ownership histories. + Check whether or not the application is used anywhere prior to removal. - :param address: The IfcApplication to remove. - :type address: ifcopenshell.entity_instance - :return: None - :rtype: None + :param address: The IfcApplication to remove. + :type address: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - application = ifcopenshell.api.run("owner.add_application", model) - ifcopenshell.api.run("owner.remove_address", model, application=application) - """ - self.file = file - self.settings = {"application": application} + application = ifcopenshell.api.run("owner.add_application", model) + ifcopenshell.api.run("owner.remove_address", model, application=application) + """ + settings = {"application": application} - def execute(self): - self.file.remove(self.settings["application"]) + file.remove(settings["application"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py index e9e2c77e9e..42111e017e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py @@ -19,53 +19,50 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, organisation=None): - """Remove an organisation +def remove_organisation(file, organisation=None) -> None: + """Remove an organisation - All roles and addresses assigned to the organisation will also be - removed. + All roles and addresses assigned to the organisation will also be + removed. - :param organisation: The IfcOrganization to remove - :type organisation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param organisation: The IfcOrganization to remove + :type organisation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation) - """ - self.file = file - self.settings = {"organisation": organisation} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation) + """ + settings = {"organisation": organisation} - def execute(self): - for role in self.settings["organisation"].Roles or []: - if len(self.file.get_inverse(role)) == 1: - ifcopenshell.api.run("owner.remove_role", self.file, role=role) - for address in self.settings["organisation"].Addresses or []: - if len(self.file.get_inverse(address)) == 1: - ifcopenshell.api.run("owner.remove_address", self.file, address=address) - for inverse in self.file.get_inverse(self.settings["organisation"]): - if inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatingOrganization == self.settings["organisation"]: - self.file.remove(inverse) - elif inverse.RelatedOrganizations == (self.settings["organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["organisation"],): - inverse.Editors = None - elif inverse.is_a("IfcPersonAndOrganization"): - ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse) - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatedResourceObjects == (self.settings["organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcApplication"): - ifcopenshell.api.run("owner.remove_application", self.file, application=inverse) + for role in settings["organisation"].Roles or []: + if len(file.get_inverse(role)) == 1: + ifcopenshell.api.run("owner.remove_role", file, role=role) + for address in settings["organisation"].Addresses or []: + if len(file.get_inverse(address)) == 1: + ifcopenshell.api.run("owner.remove_address", file, address=address) + for inverse in file.get_inverse(settings["organisation"]): + if inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatingOrganization == settings["organisation"]: + file.remove(inverse) + elif inverse.RelatedOrganizations == (settings["organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["organisation"],): + inverse.Editors = None + elif inverse.is_a("IfcPersonAndOrganization"): + ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse) + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatedResourceObjects == (settings["organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcApplication"): + ifcopenshell.api.run("owner.remove_application", file, application=inverse) - self.file.remove(self.settings["organisation"]) + file.remove(settings["organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py index 8e1ba7a972..aac583b22e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py @@ -19,51 +19,48 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, person=None): - """Remove an person +def remove_person(file, person=None) -> None: + """Remove an person - All roles and addresses assigned to the person will also be - removed. + All roles and addresses assigned to the person will also be + removed. - :param person: The IfcPerson to remove - :type person: ifcopenshell.entity_instance - :return: None - :rtype: None + :param person: The IfcPerson to remove + :type person: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - ifcopenshell.api.run("owner.remove_person", model, person=person) - """ - self.file = file - self.settings = {"person": person} + ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + ifcopenshell.api.run("owner.remove_person", model, person=person) + """ + settings = {"person": person} - def execute(self): - for role in self.settings["person"].Roles or []: - if len(self.file.get_inverse(role)) == 1: - ifcopenshell.api.run("owner.remove_role", self.file, role=role) - for address in self.settings["person"].Addresses or []: - if len(self.file.get_inverse(address)) == 1: - ifcopenshell.api.run("owner.remove_address", self.file, address=address) - for inverse in self.file.get_inverse(self.settings["person"]): - if inverse.is_a("IfcWorkControl"): - if inverse.Creators == (self.settings["person"],): - inverse.Creators = None - elif inverse.is_a("IfcInventory"): - if inverse.ResponsiblePersons == (self.settings["person"],): - inverse.ResponsiblePersons = None - elif inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["person"],): - inverse.Editors = None - elif inverse.is_a("IfcPersonAndOrganization"): - ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse) - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship"): - if inverse.RelatedResourceObjects == (self.settings["person"],): - self.file.remove(inverse) - self.file.remove(self.settings["person"]) + for role in settings["person"].Roles or []: + if len(file.get_inverse(role)) == 1: + ifcopenshell.api.run("owner.remove_role", file, role=role) + for address in settings["person"].Addresses or []: + if len(file.get_inverse(address)) == 1: + ifcopenshell.api.run("owner.remove_address", file, address=address) + for inverse in file.get_inverse(settings["person"]): + if inverse.is_a("IfcWorkControl"): + if inverse.Creators == (settings["person"],): + inverse.Creators = None + elif inverse.is_a("IfcInventory"): + if inverse.ResponsiblePersons == (settings["person"],): + inverse.ResponsiblePersons = None + elif inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["person"],): + inverse.Editors = None + elif inverse.is_a("IfcPersonAndOrganization"): + ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse) + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship"): + if inverse.RelatedResourceObjects == (settings["person"],): + file.remove(inverse) + file.remove(settings["person"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py index fc85722e68..8473e04b87 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py @@ -19,45 +19,42 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, person_and_organisation=None): - """Removes a person and organisation +def remove_person_and_organisation(file, person_and_organisation=None) -> None: + """Removes a person and organisation - Note that the underlying person and organisation is not removed, only - the "person and organisation" group. + Note that the underlying person and organisation is not removed, only + the "person and organisation" group. - :param person_and_organisation: The IfcPersonAndOrganization to remove. - :type person_and_organisation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param person_and_organisation: The IfcPersonAndOrganization to remove. + :type person_and_organisation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") + person = ifcopenshell.api.run("owner.add_person", model, + identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") - user = ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) + user = ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) - ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user) - """ - self.file = file - self.settings = {"person_and_organisation": person_and_organisation} + ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user) + """ + settings = {"person_and_organisation": person_and_organisation} - def execute(self): - for inverse in self.file.get_inverse(self.settings["person_and_organisation"]): - if inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["person_and_organisation"],): - inverse.Editors = None - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship"): - if inverse.RelatedResourceObjects == (self.settings["person_and_organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcOwnerHistory"): - self.file.remove(inverse) - self.file.remove(self.settings["person_and_organisation"]) + for inverse in file.get_inverse(settings["person_and_organisation"]): + if inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["person_and_organisation"],): + inverse.Editors = None + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship"): + if inverse.RelatedResourceObjects == (settings["person_and_organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcOwnerHistory"): + file.remove(inverse) + file.remove(settings["person_and_organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py index 5de9915c33..08bf39881c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, role=None): - """Removes a role +def remove_role(file, role=None) -> None: + """Removes a role - People and organisations using the role will be untouched. This may - leave some of them without roles. + People and organisations using the role will be untouched. This may + leave some of them without roles. - :param role: The IfcActorRole to remove. - :type role: ifcopenshell.entity_instance - :return: None - :rtype: None + :param role: The IfcActorRole to remove. + :type role: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - # After running this, the organisation will have no role again - ifcopenshell.api.run("owner.remove_role", model, role=role) - """ - self.file = file - self.settings = {"role": role} + # After running this, the organisation will have no role again + ifcopenshell.api.run("owner.remove_role", model, role=role) + """ + settings = {"role": role} - def execute(self): - for inverse in self.file.get_inverse(self.settings["role"]): - if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"): - if inverse.Roles == (self.settings["role"],): - inverse.Roles = None - elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatedResourceObjects == (self.settings["organisation"],): - self.file.remove(inverse) - self.file.remove(self.settings["role"]) + for inverse in file.get_inverse(settings["role"]): + if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"): + if inverse.Roles == (settings["role"],): + inverse.Roles = None + elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatedResourceObjects == (settings["organisation"],): + file.remove(inverse) + file.remove(settings["role"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py index 711732bcdc..c57d0172f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py @@ -21,58 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_actor=None, related_object=None): - """Unassigns an actor to an object +def unassign_actor(file, relating_actor=None, related_object=None) -> None: + """Unassigns an actor to an object - This means that the actor is no longer responsible for the object. + This means that the actor is no longer responsible for the object. - :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance - :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance - :return: The updated IfcRelAssignsToActor relationship or none if there - is no more valid relationship. - :rtype: None, ifcopenshell.entity_instance + :param relating_actor: The IfcActor who is responsible for the object. + :type relating_actor: ifcopenshell.entity_instance + :param related_object: The object the actor is responsible for. + :type related_object: ifcopenshell.entity_instance + :return: The updated IfcRelAssignsToActor relationship or none if there + is no more valid relationship. + :rtype: None, ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We need to procure and install 2 of this particular pump type in our facility. - pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") + # We need to procure and install 2 of this particular pump type in our facility. + pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") - # Define who the manufacturer is - manufacturer = ifcopenshell.api.run("owner.add_organisation", model, - identification="PWP", name="Pumps With Power") - ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") + # Define who the manufacturer is + manufacturer = ifcopenshell.api.run("owner.add_organisation", model, + identification="PWP", name="Pumps With Power") + ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") - # Make the manufacturer responsible for that pump type. - ifcopenshell.api.run("owner.assign_actor", model, - relating_actor=manufacturer, related_object=pump_type) + # Make the manufacturer responsible for that pump type. + ifcopenshell.api.run("owner.assign_actor", model, + relating_actor=manufacturer, related_object=pump_type) - # Undo the assignment - ifcopenshell.api.run("owner.unassign_actor", model, - relating_actor=manufacturer, related_object=pump_type) - """ - self.file = file - self.settings = { - "relating_actor": relating_actor, - "related_object": related_object, - } + # Undo the assignment + ifcopenshell.api.run("owner.unassign_actor", model, + relating_actor=manufacturer, related_object=pump_type) + """ + settings = { + "relating_actor": relating_actor, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != self.settings["relating_actor"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != settings["relating_actor"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py index 797d1b8b57..95a356ef2f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py @@ -24,71 +24,70 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__(self, file: ifcopenshell.file, element: ifcopenshell.entity_instance): - """Updates the owner that is assigned to an object +def update_owner_history( + file: ifcopenshell.file, element: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, None]: + """Updates the owner that is assigned to an object - This ensures that the owner is tracked to have modified the object last, - including the time when the change occured. See - ifcopenshell.api.owner.create_owner_history for details. + This ensures that the owner is tracked to have modified the object last, + including the time when the change occured. See + ifcopenshell.api.owner.create_owner_history for details. - :param element: The IfcRoot element to update the ownership details on - when a change is made. - :type element: ifcopenshell.entity_instance - :return: The updated IfcOwnerHistory element. - :rtype: ifcopenshell.entity_instance + :param element: The IfcRoot element to update the ownership details on + when a change is made. + :type element: ifcopenshell.entity_instance + :return: The updated IfcOwnerHistory element. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # See ifcopenshell.api.owner.create_owner_history for setup - # [ ... example setup code ... ] + # See ifcopenshell.api.owner.create_owner_history for setup + # [ ... example setup code ... ] - # We've finished our ownership setup. Now let's start our script and - # create a space. Notice we don't actually call - # create_owner_history at all. This is already automatically handled - # by the API when necessary. Under the hood, the API is actually - # running this code on the IfcSpace element: - # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + # We've finished our ownership setup. Now let's start our script and + # create a space. Notice we don't actually call + # create_owner_history at all. This is already automatically handled + # by the API when necessary. Under the hood, the API is actually + # running this code on the IfcSpace element: + # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - # Any edits we make will have ownership tracking automatically - # applied. There is no need to run any owner.update_owner_history - # API calls either. - ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"}) - """ - self.file = file - self.settings = {"element": element} + # Any edits we make will have ownership tracking automatically + # applied. There is no need to run any owner.update_owner_history + # API calls either. + ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"}) + """ + settings = {"element": element} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - element = self.settings["element"] - if not element.is_a("IfcRoot"): - return - user = ifcopenshell.api.owner.settings.get_user(self.file) - if not user: - return - application = ifcopenshell.api.owner.settings.get_application(self.file) - if not application: - return + element = settings["element"] + if not element.is_a("IfcRoot"): + return + user = ifcopenshell.api.owner.settings.get_user(file) + if not user: + return + application = ifcopenshell.api.owner.settings.get_application(file) + if not application: + return - # 1 IfcRoot IfcOwnerHistory - owner_history = element[1] - if not owner_history: - owner_history = ifcopenshell.api.run("owner.create_owner_history", self.file) - element[1] = owner_history - return owner_history - - if self.file.get_total_inverses(owner_history) > 1: - owner_history = ifcopenshell.util.element.copy(self.file, owner_history) - element[1] = owner_history - - # 3 IfcOwnerHistory ChangeAction - owner_history[3] = "MODIFIED" - # 4 IfcOwnerHistory LastModifiedDate - owner_history[4] = int(time.time()) - # 5 IfcOwnerHistory LastModifyingUser - owner_history[5] = user - # 6 IfcOwnerHistory LastModifyingApplication - owner_history[6] = application + # 1 IfcRoot IfcOwnerHistory + owner_history = element[1] + if not owner_history: + owner_history = ifcopenshell.api.run("owner.create_owner_history", file) + element[1] = owner_history return owner_history + + if file.get_total_inverses(owner_history) > 1: + owner_history = ifcopenshell.util.element.copy(file, owner_history) + element[1] = owner_history + + # 3 IfcOwnerHistory ChangeAction + owner_history[3] = "MODIFIED" + # 4 IfcOwnerHistory LastModifiedDate + owner_history[4] = int(time.time()) + # 5 IfcOwnerHistory LastModifyingUser + owner_history[5] = user + # 6 IfcOwnerHistory LastModifyingApplication + owner_history[6] = application + return owner_history diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py index e0caddbe3c..7decc6750b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_arbitrary_profile import add_arbitrary_profile +from .add_arbitrary_profile_with_voids import add_arbitrary_profile_with_voids +from .add_parameterized_profile import add_parameterized_profile +from .edit_profile import edit_profile +from .remove_profile import remove_profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py index 8cced9d934..9acc800b89 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py @@ -19,39 +19,42 @@ import ifcopenshell.util.unit +def add_arbitrary_profile(file, profile=None, name=None) -> None: + """Adds a new arbitrary polyline-based profile + + The profile is represented as a polyline defined by a list of + coordinates. Only straight segments are allowed. Coordinates must be + provided in SI meters. + + To represent a closed curve, the first and last coordinate must be + identical. + + :param profile: A list of coordinates + :type profile: list[list[float]] + :param name: If the profile is semantically significant (i.e. to be + managed and reused by the user) then it must be named. Otherwise, + this may be left as none. + :type name: str, optional + :return: The newly created IfcArbitraryClosedProfileDef + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A 10mm by 100mm rectangle, such that might be used as a wooden + # skirting board or kick plate. + square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, + profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)], + name="SK01 Profile") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"profile": profile, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, profile=None, name=None): - """Adds a new arbitrary polyline-based profile - - The profile is represented as a polyline defined by a list of - coordinates. Only straight segments are allowed. Coordinates must be - provided in SI meters. - - To represent a closed curve, the first and last coordinate must be - identical. - - :param profile: A list of coordinates - :type profile: list[list[float]] - :param name: If the profile is semantically significant (i.e. to be - managed and reused by the user) then it must be named. Otherwise, - this may be left as none. - :type name: str, optional - :return: The newly created IfcArbitraryClosedProfileDef - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A 10mm by 100mm rectangle, such that might be used as a wooden - # skirting board or kick plate. - square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, - profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)], - name="SK01 Profile") - """ - self.file = file - self.settings = {"profile": profile, "name": name} - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) points = [self.convert_si_to_unit(p) for p in self.settings["profile"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py index 7c1af83294..e1a6fe9cdb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py @@ -19,46 +19,49 @@ import ifcopenshell.util.unit +def add_arbitrary_profile_with_voids(file, outer_profile=None, inner_profiles=None, name=None) -> None: + """Adds a new arbitrary polyline-based profile with voids + + The outer profile is represented as a polyline defined by a list of + coordinates. Only straight segments are allowed. Coordinates must be + provided in SI meters. + + To represent a closed curve, the first and last coordinate must be + identical. + + The inner profiles are represented as a list of polylines. + Every polyline in defined by a list of coordinates. + Only straight segments are allowed. Coordinates must be + provided in SI meters. + + :param outer_profile: A list of coordinates + :type profile: list[float] + :param inner_profiles: A list of polylines + :type profile: list[list[float]] + :param name: If the profile is semantically significant (i.e. to be + managed and reused by the user) then it must be named. Otherwise, + this may be left as none. + :type name: str, optional + :return: The newly created IfcArbitraryProfileDefWithVoids + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A 400mm by 400mm square with a 200mm by 200mm hole in it. + square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model, + outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)], + inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]], + name="SK01 Hole Profile") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, outer_profile=None, inner_profiles=None, name=None): - """Adds a new arbitrary polyline-based profile with voids - - The outer profile is represented as a polyline defined by a list of - coordinates. Only straight segments are allowed. Coordinates must be - provided in SI meters. - - To represent a closed curve, the first and last coordinate must be - identical. - - The inner profiles are represented as a list of polylines. - Every polyline in defined by a list of coordinates. - Only straight segments are allowed. Coordinates must be - provided in SI meters. - - :param outer_profile: A list of coordinates - :type profile: list[float] - :param inner_profiles: A list of polylines - :type profile: list[list[float]] - :param name: If the profile is semantically significant (i.e. to be - managed and reused by the user) then it must be named. Otherwise, - this may be left as none. - :type name: str, optional - :return: The newly created IfcArbitraryProfileDefWithVoids - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A 400mm by 400mm square with a 200mm by 200mm hole in it. - square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model, - outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)], - inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]], - name="SK01 Hole Profile") - """ - self.file = file - self.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name} - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) outer_points = [self.convert_si_to_unit(p) for p in self.settings["outer_profile"]] @@ -69,7 +72,9 @@ class Usecase: outer_curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in outer_points]) inner_curves = [] for inner_point in inner_points: - inner_curves.append(self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point])) + inner_curves.append( + self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point]) + ) else: outer_curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(outer_points)) inner_curves = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py index 0201095feb..c0a6348656 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, ifc_class=None): - """Adds a new parameterised profile +def add_parameterized_profile(file, ifc_class=None) -> None: + """Adds a new parameterised profile - IFC offers parameterised profiles for common standardised hot roll - steel sections and common concrete forms. A full list is available on - the IFC documentation as subclasses of IfcParameterizedProfileDef. + IFC offers parameterised profiles for common standardised hot roll + steel sections and common concrete forms. A full list is available on + the IFC documentation as subclasses of IfcParameterizedProfileDef. - Currently, this API has no benefit over directly calling - ifcopenshell.file.create_entity. + Currently, this API has no benefit over directly calling + ifcopenshell.file.create_entity. - :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd - like to create. - :type ifc_class: str - :return: The newly created element depending on the specified ifc_class. - :rtype: ifcopenshell.entity_instance + :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd + like to create. + :type ifc_class: str + :return: The newly created element depending on the specified ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle.Radius = 1. - """ - self.file = file - self.settings = {"ifc_class": ifc_class} + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle.Radius = 1. + """ + settings = {"ifc_class": ifc_class} - def execute(self): - return self.file.create_entity(self.settings["ifc_class"]) + return file.create_entity(settings["ifc_class"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py index 759a5d4c7d..4d525a5d32 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, profile=None, attributes=None): - """Edits the attributes of an IfcProfileDef +def edit_profile(file, profile=None, attributes=None) -> None: + """Edits the attributes of an IfcProfileDef - For more information about the attributes and data types of an - IfcProfileDef, consult the IFC documentation. + For more information about the attributes and data types of an + IfcProfileDef, consult the IFC documentation. - :param profile: The IfcProfileDef entity you want to edit - :type profile: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param profile: The IfcProfileDef entity you want to edit + :type profile: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle = 1. + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle = 1. - ifcopenshell.api.run("profile.edit_profile", model, - profile=circle, attributes={"ProfileName": "1000mm Dia"}) - """ - self.file = file - self.settings = {"profile": profile, "attributes": attributes or {}} + ifcopenshell.api.run("profile.edit_profile", model, + profile=circle, attributes={"ProfileName": "1000mm Dia"}) + """ + settings = {"profile": profile, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["profile"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["profile"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py index 47d3391e00..58f3eebedb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py @@ -20,32 +20,29 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, profile=None): - """Removes a profile +def remove_profile(file, profile=None) -> None: + """Removes a profile - :param profile: The IfcProfileDef to remove. - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None + :param profile: The IfcProfileDef to remove. + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle = 1. - ifcopenshell.api.run("profile.remove_profile", model, profile=circle) - """ - self.file = file - self.settings = {"profile": profile} + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle = 1. + ifcopenshell.api.run("profile.remove_profile", model, profile=circle) + """ + settings = {"profile": profile} - def execute(self): - subelements = set() - for attribute in self.settings["profile"]: - if isinstance(attribute, ifcopenshell.entity_instance): - subelements.add(attribute) - self.file.remove(self.settings["profile"]) - for subelement in subelements: - ifcopenshell.util.element.remove_deep2(self.file, subelement) + subelements = set() + for attribute in settings["profile"]: + if isinstance(attribute, ifcopenshell.entity_instance): + subelements.add(attribute) + file.remove(settings["profile"]) + for subelement in subelements: + ifcopenshell.util.element.remove_deep2(file, subelement) diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py index e0caddbe3c..e9c21fbddd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .append_asset import append_asset +from .assign_declaration import assign_declaration +from .create_file import create_file +from .unassign_declaration import unassign_declaration diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index 3e88c7c82a..fc101347ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -21,100 +21,103 @@ import ifcopenshell.api import ifcopenshell.api.owner.settings +def append_asset(file, library=None, element=None, reuse_identities=None) -> None: + """Appends an asset from a library into the active project + + A BIM library asset may be a type product (e.g. wall type), product + (e.g. pump), material, profile, or cost schedule. + + This copies the asset from the specified library file into the active + project. It handles all details like ensuring that product materials, + styles, properties, quantities, and so on are preserved. + + If an asset contains geometry, the geometric contexts are also + intelligentely transplanted such that existing equivalent contexts are + reused. + + Do not mix units. + + :param library: The file object containing the asset. + :type library: ifcopenshell.file + :param element: An element in the library file of the asset. It may be + an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or + IfcProfileDef. + :type element: ifcopenshell.entity_instance + :param reuse_identities: Optional dictionary of mapped entities' identities to the + already created elements. It will be used to avoid creating + duplicated inverse elements during multiple `project.append_asset` calls. If you want + to add just 1 asset or if added assets won't have any shared elements, then it can be left empty. + :type reuse_identities: dict[int, ifcopenshell.entity_instance] + :return: The appended element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + + # Assign units for our example library + unit = ifcopenshell.api.run("unit.add_si_unit", library, + unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") + ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) + + # Let's create a single asset of a 200mm thick concrete wall + wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") + concrete = ifcopenshell.api.run("material.add_material", usecase.file, name="CON", category="concrete") + rel = ifcopenshell.api.run("material.assign_material", library, + products=[wall_type], type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", library, + layer_set=rel.RelatingMaterial, material=concrete) + layer.Name = "Structure" + layer.LayerThickness = 200 + + # Mark our wall type as a reusable asset in our library. + ifcopenshell.api.run("project.assign_declaration", library, + definitions=[wall_type], relating_context=context) + + # Let's imagine we're starting a new project + model = ifcopenshell.api.run("project.create_file") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") + + # Now we can easily append our wall type from our libary + wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type) + + Example of adding multiple assets and avoiding duplicated inverses: + + .. code:: python + + # since occurrences of IfcWindow of the same type + # might have shared inverses (e.g. IfcStyledItem) + # we provide a dictionary that will be populated with newly created items + # and reused to avoid duplicated elements + reuse_identities = dict() + + for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"): + ifcopenshell.api.run( + "project.append_asset", + model, library=library, + element=wall_type + reuse_identities=reuse_identities + ) + + """ + usecase = Usecase() + usecase.file: ifcopenshell.file = file + usecase.settings = { + "library": library, + "element": element, + "reuse_identities": {} if reuse_identities is None else reuse_identities, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, library=None, element=None, reuse_identities=None): - """Appends an asset from a library into the active project - - A BIM library asset may be a type product (e.g. wall type), product - (e.g. pump), material, profile, or cost schedule. - - This copies the asset from the specified library file into the active - project. It handles all details like ensuring that product materials, - styles, properties, quantities, and so on are preserved. - - If an asset contains geometry, the geometric contexts are also - intelligentely transplanted such that existing equivalent contexts are - reused. - - Do not mix units. - - :param library: The file object containing the asset. - :type library: ifcopenshell.file - :param element: An element in the library file of the asset. It may be - an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or - IfcProfileDef. - :type element: ifcopenshell.entity_instance - :param reuse_identities: Optional dictionary of mapped entities' identities to the - already created elements. It will be used to avoid creating - duplicated inverse elements during multiple `project.append_asset` calls. If you want - to add just 1 asset or if added assets won't have any shared elements, then it can be left empty. - :type reuse_identities: dict[int, ifcopenshell.entity_instance] - :return: The appended element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - - # Assign units for our example library - unit = ifcopenshell.api.run("unit.add_si_unit", library, - unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") - ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) - - # Let's create a single asset of a 200mm thick concrete wall - wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") - concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete") - rel = ifcopenshell.api.run("material.assign_material", library, - products=[wall_type], type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", library, - layer_set=rel.RelatingMaterial, material=concrete) - layer.Name = "Structure" - layer.LayerThickness = 200 - - # Mark our wall type as a reusable asset in our library. - ifcopenshell.api.run("project.assign_declaration", library, - definitions=[wall_type], relating_context=context) - - # Let's imagine we're starting a new project - model = ifcopenshell.api.run("project.create_file") - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") - - # Now we can easily append our wall type from our libary - wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type) - - Example of adding multiple assets and avoiding duplicated inverses: - - .. code:: python - - # since occurrences of IfcWindow of the same type - # might have shared inverses (e.g. IfcStyledItem) - # we provide a dictionary that will be populated with newly created items - # and reused to avoid duplicated elements - reuse_identities = dict() - - for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"): - ifcopenshell.api.run( - "project.append_asset", - model, library=library, - element=wall_type - reuse_identities=reuse_identities - ) - - """ - self.file: ifcopenshell.file = file - self.settings = { - "library": library, - "element": element, - "reuse_identities": {} if reuse_identities is None else reuse_identities, - } - def execute(self): # mapping of old element ids to new elements self.added_elements: dict[int, ifcopenshell.entity_instance] = {} diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py index e6e919b77b..776a26b8f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -22,129 +22,125 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - definitions: list[ifcopenshell.entity_instance], - relating_context: ifcopenshell.entity_instance, - ): - """Declares the list of elements to the project +def assign_declaration( + file: ifcopenshell.entity_instance, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Declares the list of elements to the project - All data in a model must be directly or indirectly related to the - project. Most data is indirectly related, existing instead within the - spatial decomposition tree. Other data, such as types, may be declared - at the top level. + All data in a model must be directly or indirectly related to the + project. Most data is indirectly related, existing instead within the + spatial decomposition tree. Other data, such as types, may be declared + at the top level. - Most of the time, the API handles declaration automatically for you. - There is one scenario where you might want to explicitly declare objects - to the project, and that's when you want to organise objects into - project libraries for future use (such as an assets library). Assigning - a declaration lets you say that an object belongs to a library. + Most of the time, the API handles declaration automatically for you. + There is one scenario where you might want to explicitly declare objects + to the project, and that's when you want to organise objects into + project libraries for future use (such as an assets library). Assigning + a declaration lets you say that an object belongs to a library. - :param definitions: The list of objects you want to declare. Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance] - :param relating_context: The IfcProject, or more commonly the - IfcProjectLibrary that you want the object to be part of. - :type relating_context: ifcopenshell.entity_instance - :return: The new IfcRelDeclares relationship or None if all definitions - were already declared / do not support declaration. - :rtype: Union[ifcopenshell.entity_instance, None] + :param definitions: The list of objects you want to declare. Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance] + :param relating_context: The IfcProject, or more commonly the + IfcProjectLibrary that you want the object to be part of. + :type relating_context: ifcopenshell.entity_instance + :return: The new IfcRelDeclares relationship or None if all definitions + were already declared / do not support declaration. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") - # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + # It's necessary to say our library is part of our project. + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - # Assign units for our example library - unit = ifcopenshell.api.run("unit.add_si_unit", library, - unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") - ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) + # Assign units for our example library + unit = ifcopenshell.api.run("unit.add_si_unit", library, + unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") + ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) - # Let's create a single asset of a 200mm thick concrete wall - wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") - concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete") - rel = ifcopenshell.api.run("material.assign_material", library, - products=[wall_type], type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", library, - layer_set=rel.RelatingMaterial, material=concrete) - layer.Name = "Structure" - layer.LayerThickness = 200 + # Let's create a single asset of a 200mm thick concrete wall + wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") + concrete = ifcopenshell.api.run("material.add_material", file, name="CON", category="concrete") + rel = ifcopenshell.api.run("material.assign_material", library, + products=[wall_type], type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", library, + layer_set=rel.RelatingMaterial, material=concrete) + layer.Name = "Structure" + layer.LayerThickness = 200 - # Mark our wall type as a reusable asset in our library. - ifcopenshell.api.run("project.assign_declaration", library, - definitions=[wall_type], relating_context=context) + # Mark our wall type as a reusable asset in our library. + ifcopenshell.api.run("project.assign_declaration", library, + definitions=[wall_type], relating_context=context) - # All done, just for fun let's save our asset library to disk for later use. - library.write("/path/to/my-library.ifc") - """ - self.file = file - self.settings = { - "definitions": definitions, - "relating_context": relating_context, - } + # All done, just for fun let's save our asset library to disk for later use. + library.write("/path/to/my-library.ifc") + """ + settings = { + "definitions": definitions, + "relating_context": relating_context, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - relating_context = self.settings["relating_context"] - all_declares = relating_context.Declares - definitions = set(self.settings["definitions"]) + relating_context = settings["relating_context"] + all_declares = relating_context.Declares + definitions = set(settings["definitions"]) - previous_declares_rels: set[ifcopenshell.entity_instance] = set() - objects_without_contexts: list[ifcopenshell.entity_instance] = [] - objects_with_contexts: list[ifcopenshell.entity_instance] = [] + previous_declares_rels: set[ifcopenshell.entity_instance] = set() + objects_without_contexts: list[ifcopenshell.entity_instance] = [] + objects_with_contexts: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for definition in definitions: - has_context = getattr(definition, "HasContext", None) - if has_context is None: - continue + # check if there is anything to change + for definition in definitions: + has_context = getattr(definition, "HasContext", None) + if has_context is None: + continue - object_rel = next(iter(has_context), None) - if object_rel is None: - objects_without_contexts.append(definition) - continue + object_rel = next(iter(has_context), None) + if object_rel is None: + objects_without_contexts.append(definition) + continue - # either rel doesn't exist or product is part of different rel - if object_rel not in all_declares: - previous_declares_rels.add(object_rel) - objects_with_contexts.append(definition) + # either rel doesn't exist or product is part of different rel + if object_rel not in all_declares: + previous_declares_rels.add(object_rel) + objects_with_contexts.append(definition) - objects_to_change = objects_without_contexts + objects_with_contexts - # nothing to change - if not objects_to_change: - return None + objects_to_change = objects_without_contexts + objects_with_contexts + # nothing to change + if not objects_to_change: + return None - for has_context in previous_declares_rels: - related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts - if related_definitions: - has_context.RelatedDefinitions = related_definitions - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_context}) - else: - history = has_context.OwnerHistory - self.file.remove(has_context) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - declares = next(iter(all_declares), None) - if declares: - declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change)) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": declares}) + for has_context in previous_declares_rels: + related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts + if related_definitions: + has_context.RelatedDefinitions = related_definitions + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": has_context}) else: - declares = self.file.create_entity( - "IfcRelDeclares", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedDefinitions": list(objects_to_change), - "RelatingContext": relating_context, - } - ) - return declares + history = has_context.OwnerHistory + file.remove(has_context) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + declares = next(iter(all_declares), None) + if declares: + declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change)) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": declares}) + else: + declares = file.create_entity( + "IfcRelDeclares", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedDefinitions": list(objects_to_change), + "RelatingContext": relating_context, + } + ) + return declares diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py index 1b3e644cbd..6db9e87c01 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py @@ -20,52 +20,46 @@ import datetime import ifcopenshell -class Usecase: - def __init__(self, version: str = "IFC4"): - """Create a blank IFC model file object +def create_file(version: str = "IFC4") -> ifcopenshell.file: + """Create a blank IFC model file object - Create a new IFC file object based on the nominated schema version. The - schema version you choose determines what type of IFC data you can store - in this model. The file is blank and contains no entities. + Create a new IFC file object based on the nominated schema version. The + schema version you choose determines what type of IFC data you can store + in this model. The file is blank and contains no entities. - It also sets up header data for STEP file serialisation, such as the - current timestamp, IfcOpenShell as the preprocessor, and defaults to a - DesignTransferView MVD. + It also sets up header data for STEP file serialisation, such as the + current timestamp, IfcOpenShell as the preprocessor, and defaults to a + DesignTransferView MVD. - :param version: The schema version of the IFC file. Choose from - "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom - schema, you may specify that schema identifier here too. - :type version: str, optional - :return: The created IFC file object. - :rtype: ifcopenshell.file + :param version: The schema version of the IFC file. Choose from + "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom + schema, you may specify that schema identifier here too. + :type version: str, optional + :return: The created IFC file object. + :rtype: ifcopenshell.file - Example: + Example: - .. code:: python + .. code:: python - # Start a new model. - model = ifcopenshell.api.run("project.create_file") + # Start a new model. + model = ifcopenshell.api.run("project.create_file") - # It's currently a blank model, so typically the first thing we do - # is create a project in it. - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") + # It's currently a blank model, so typically the first thing we do + # is create a project in it. + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") - # ... and off we go! - """ - self.settings = {"version": version} + # ... and off we go! + """ + settings = {"version": version} - def execute(self) -> ifcopenshell.file: - self.file = ifcopenshell.file(schema=self.settings["version"]) - self.file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe - self.file.wrapped_data.header.file_name.time_stamp = ( - datetime.datetime.utcnow() - .replace(tzinfo=datetime.timezone.utc) - .astimezone() - .replace(microsecond=0) - .isoformat() - ) - self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version) - self.file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version) - self.file.wrapped_data.header.file_name.authorization = "Nobody" - self.file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",) - return self.file + file = ifcopenshell.file(schema=settings["version"]) + file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe + file.wrapped_data.header.file_name.time_stamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat() + ) + file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version) + file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version) + file.wrapped_data.header.file_name.authorization = "Nobody" + file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",) + return file diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py index 8f8e726570..38a0ae4e25 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -21,59 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - definitions: list[ifcopenshell.entity_instance], - relating_context: ifcopenshell.entity_instance, - ): - """Unassigns a list of objects from a project or project library +def unassign_declaration( + file: ifcopenshell.file, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, +) -> None: + """Unassigns a list of objects from a project or project library - Typically used to remove an asset from a project library. + Typically used to remove an asset from a project library. - :param definitions: The list of objects you want to undeclare. - Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance] - :param relating_context: The IfcProject, or more commonly the - IfcProjectLibrary that you want the object to no longer be part of. - :type relating_context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param definitions: The list of objects you want to undeclare. + Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance] + :param relating_context: The IfcProject, or more commonly the + IfcProjectLibrary that you want the object to no longer be part of. + :type relating_context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") - # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + # It's necessary to say our library is part of our project. + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - # Remove the library from our project - ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root) - """ - self.file = file - self.settings = { - "definitions": definitions, - "relating_context": relating_context, - } + # Remove the library from our project + ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root) + """ + settings = { + "definitions": definitions, + "relating_context": relating_context, + } - def execute(self): - definitions = set(self.settings["definitions"]) - rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))} + definitions = set(settings["definitions"]) + rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))} - for rel in rels: - related_definitions = set(rel.RelatedDefinitions) - definitions - if related_definitions: - rel.RelatedDefinitions = list(related_definitions) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_definitions = set(rel.RelatedDefinitions) - definitions + if related_definitions: + rel.RelatedDefinitions = list(related_definitions) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py index e0caddbe3c..c3e01e30df 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_pset import add_pset +from .add_qto import add_qto +from .edit_pset import edit_pset +from .edit_qto import edit_qto +from .remove_pset import remove_pset diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py index 1fd1a0c61a..6cb72e435c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py @@ -19,133 +19,127 @@ import ifcopenshell -class Usecase: - def __init__(self, file, product=None, name=None): - """Adds a new property set to a product +def add_pset(file, product=None, name=None) -> None: + """Adds a new property set to a product - Products, such as physical objects or types in IFC may have properties - associated with them. These properties are typically simple key value - metadata with data types. For example, a wall type may have a property - called FireRating with a text value of "2HR". Properties are grouped - into property sets, so that related properties are grouped together. + Products, such as physical objects or types in IFC may have properties + associated with them. These properties are typically simple key value + metadata with data types. For example, a wall type may have a property + called FireRating with a text value of "2HR". Properties are grouped + into property sets, so that related properties are grouped together. - If a property is assigned to a type, the property is inherited by all - occurrences of that type. For example, a wall type with a FireRating - property of "2HR" automatically implies that all walls of that wall type - also have a FireRating of "2HR". It is not necessary to explictly define - the property again for each occurrence. This also means that properties - are typically defined on types. If the same property is defined at an - occurrence, this overrides the property defined on the type. + If a property is assigned to a type, the property is inherited by all + occurrences of that type. For example, a wall type with a FireRating + property of "2HR" automatically implies that all walls of that wall type + also have a FireRating of "2HR". It is not necessary to explictly define + the property again for each occurrence. This also means that properties + are typically defined on types. If the same property is defined at an + occurrence, this overrides the property defined on the type. - buildingSMART has come up with a long list of standardised properties - for the most common properties required internationally. This solves the - age-old question of "where do I store my FireRating data for walls"? The - answer, in this case, is in the "FireRating" property with an "IfcLabel" - data type grouped in the "Pset_WallCommon" property set. It is - recommended to view the list of standardised buildingSMART properties - and see if any suit your needs first. If none are appropriate, then you - are free to create your own custom properties. + buildingSMART has come up with a long list of standardised properties + for the most common properties required internationally. This solves the + age-old question of "where do I store my FireRating data for walls"? The + answer, in this case, is in the "FireRating" property with an "IfcLabel" + data type grouped in the "Pset_WallCommon" property set. It is + recommended to view the list of standardised buildingSMART properties + and see if any suit your needs first. If none are appropriate, then you + are free to create your own custom properties. - This function adds a blank named property set. One you have a property - set you may add properties using ifcopenshell.api.pset.edit_pset. + This function adds a blank named property set. One you have a property + set you may add properties using ifcopenshell.api.pset.edit_pset. - See also ifcopenshell.api.pset.add_qto if you want to add quantification - data, rather than arbitrary metadata. + See also ifcopenshell.api.pset.add_qto if you want to add quantification + data, rather than arbitrary metadata. - :param product: The IfcObject that you want to assign a property set to. - :type product: ifcopenshell.entity_instance - :param name: The name of the property set. Property sets that are - standardised by buildingSMART typically have a prefix of "Pset_", - like "Pset_WallCommon". If you create your own, you must not use - that prefix. It is recommended to use your own prefix tailored to - your project, company, or local government requirement. - :type name: str - :return: The newly created IfcPropertySet - :rtype: ifcopenshell.entity_instance + :param product: The IfcObject that you want to assign a property set to. + :type product: ifcopenshell.entity_instance + :param name: The name of the property set. Property sets that are + standardised by buildingSMART typically have a prefix of "Pset_", + like "Pset_WallCommon". If you create your own, you must not use + that prefix. It is recommended to use your own prefix tailored to + your project, company, or local government requirement. + :type name: str + :return: The newly created IfcPropertySet + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a new wall type. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + # Let's imagine we have a new wall type. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - # Note that this only creates and assigns an empty property set. We - # still need to add properties into the property set. Having blank - # property sets are invalid. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + # Note that this only creates and assigns an empty property set. We + # still need to add properties into the property set. Having blank + # property sets are invalid. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - # Add a fire rating property standardised by buildingSMART. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"}) - """ - self.file = file - self.settings = {"product": product, "name": name} + # Add a fire rating property standardised by buildingSMART. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"}) + """ + settings = {"product": product, "name": name} - def execute(self): - if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"): - for rel in self.settings["product"].IsDefinedBy or []: - if ( - rel.is_a("IfcRelDefinesByProperties") - and rel.RelatingPropertyDefinition.Name == self.settings["name"] - ): - return rel.RelatingPropertyDefinition + if settings["product"].is_a("IfcObject") or settings["product"].is_a("IfcContext"): + for rel in settings["product"].IsDefinedBy or []: + if rel.is_a("IfcRelDefinesByProperties") and rel.RelatingPropertyDefinition.Name == settings["name"]: + return rel.RelatingPropertyDefinition - pset = self.file.create_entity( - "IfcPropertySet", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["name"], - } - ) - self.file.create_entity( - "IfcRelDefinesByProperties", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], - "RelatingPropertyDefinition": pset, - } - ) - return pset - elif self.settings["product"].is_a("IfcTypeObject"): - for definition in self.settings["product"].HasPropertySets or []: - if definition.Name == self.settings["name"]: - return definition + pset = file.create_entity( + "IfcPropertySet", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["name"], + } + ) + file.create_entity( + "IfcRelDefinesByProperties", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["product"]], + "RelatingPropertyDefinition": pset, + } + ) + return pset + elif settings["product"].is_a("IfcTypeObject"): + for definition in settings["product"].HasPropertySets or []: + if definition.Name == settings["name"]: + return definition - pset = self.file.create_entity( - "IfcPropertySet", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["name"], - } - ) - has_property_sets = list(self.settings["product"].HasPropertySets or []) - has_property_sets.append(pset) - self.settings["product"].HasPropertySets = has_property_sets - return pset - elif self.settings["product"].is_a("IfcMaterialDefinition"): - for definition in self.settings["product"].HasProperties or []: - if definition.Name == self.settings["name"]: - return definition + pset = file.create_entity( + "IfcPropertySet", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["name"], + } + ) + has_property_sets = list(settings["product"].HasPropertySets or []) + has_property_sets.append(pset) + settings["product"].HasPropertySets = has_property_sets + return pset + elif settings["product"].is_a("IfcMaterialDefinition"): + for definition in settings["product"].HasProperties or []: + if definition.Name == settings["name"]: + return definition - return self.file.create_entity( - "IfcMaterialProperties", - **{ - "Name": self.settings["name"], - "Material": self.settings["product"], - } - ) - elif self.settings["product"].is_a("IfcProfileDef"): - for definition in self.settings["product"].HasProperties or []: - if definition.Name == self.settings["name"]: - return definition + return file.create_entity( + "IfcMaterialProperties", + **{ + "Name": settings["name"], + "Material": settings["product"], + } + ) + elif settings["product"].is_a("IfcProfileDef"): + for definition in settings["product"].HasProperties or []: + if definition.Name == settings["name"]: + return definition - return self.file.create_entity( - "IfcProfileProperties", - **{ - "Name": self.settings["name"], - "ProfileDefinition": self.settings["product"], - } - ) + return file.create_entity( + "IfcProfileProperties", + **{ + "Name": settings["name"], + "ProfileDefinition": settings["product"], + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py index a3c7b54299..0215ab31ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py @@ -20,65 +20,68 @@ import ifcopenshell import ifcopenshell.api +def add_qto(file, product=None, name=None) -> None: + """Adds a new quantity set to a product + + Products, such as physical objects or types in IFC may have quantities + associated with them. These quantities are typically simple key value + metadata with data types. For example, a wall type may have a quantity + called NetSideArea with a area value of "4.2". Quantities are grouped + into quantity sets, so that related quantities are grouped together. + + Quantities are similar to, but different from properties in that they + may store a method of measurement or formula. Quantities may also have + parametric relationships to other calculated values, such as cost + schedules, resource utilisation, or construction task durations. + + buildingSMART has come up with a long list of standardised quantities + for the most common quantities required internationally. This solves the + age-old question of "what's the standard way of storing quantity + take-off data"? It is recommended to view the list of standardised + buildingSMART quantities and see if any suit your needs first. If none + are appropriate, then you are free to create your own custom quantities. + + This function adds a blank named quantity set. One you have a quantity + set you may add quantities using ifcopenshell.api.pset.edit_qto. + + See also ifcopenshell.api.pset.add_qto if you want to arbitrary + metadata, rather than quantification data. + + :param product: The IfcObject that you want to assign a quantity set to. + :type product: ifcopenshell.entity_instance + :param name: The name of the quantity set. Quantity sets that are + standardised by buildingSMART typically have a prefix of "Qto_", + like "Qto_WallBaseQuantities". If you create your own, you must not + use that prefix. It is recommended to use your own prefix tailored + to your project, company, or local government requirement. + :type name: str + :return: The newly created IfcElementQuantity + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Let's imagine we have a new wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Note that this only creates and assigns an empty quantity set. We + # still need to add quantities into the property set. Having blank + # quantity sets are invalid. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities") + + # Add a side area property standardised by buildingSMART. This + # allows quantity take-off to occur, even though no geometry has + # even been modelled! + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": product, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, product=None, name=None): - """Adds a new quantity set to a product - - Products, such as physical objects or types in IFC may have quantities - associated with them. These quantities are typically simple key value - metadata with data types. For example, a wall type may have a quantity - called NetSideArea with a area value of "4.2". Quantities are grouped - into quantity sets, so that related quantities are grouped together. - - Quantities are similar to, but different from properties in that they - may store a method of measurement or formula. Quantities may also have - parametric relationships to other calculated values, such as cost - schedules, resource utilisation, or construction task durations. - - buildingSMART has come up with a long list of standardised quantities - for the most common quantities required internationally. This solves the - age-old question of "what's the standard way of storing quantity - take-off data"? It is recommended to view the list of standardised - buildingSMART quantities and see if any suit your needs first. If none - are appropriate, then you are free to create your own custom quantities. - - This function adds a blank named quantity set. One you have a quantity - set you may add quantities using ifcopenshell.api.pset.edit_qto. - - See also ifcopenshell.api.pset.add_qto if you want to arbitrary - metadata, rather than quantification data. - - :param product: The IfcObject that you want to assign a quantity set to. - :type product: ifcopenshell.entity_instance - :param name: The name of the quantity set. Quantity sets that are - standardised by buildingSMART typically have a prefix of "Qto_", - like "Qto_WallBaseQuantities". If you create your own, you must not - use that prefix. It is recommended to use your own prefix tailored - to your project, company, or local government requirement. - :type name: str - :return: The newly created IfcElementQuantity - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Let's imagine we have a new wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Note that this only creates and assigns an empty quantity set. We - # still need to add quantities into the property set. Having blank - # quantity sets are invalid. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities") - - # Add a side area property standardised by buildingSMART. This - # allows quantity take-off to occur, even though no geometry has - # even been modelled! - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2}) - """ - self.file = file - self.settings = {"product": product, "name": name} - def execute(self): if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"): for rel in self.settings["product"].IsDefinedBy or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index 0eb711b803..c4955e1605 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -20,141 +20,144 @@ import ifcopenshell import ifcopenshell.util.pset +def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, should_purge=False) -> None: + """Edits a property set and its properties + + At its simplest usage, this may be used to edit the name of a property + set. It may also be used to add, edit, or remove properties, either + arbitrarily or using a property set template. + + A list of properties are provided as a dictionary, where the keys are + property names, and values are property values. Keys that don't already + exist are interpreted as properties to be added. Keys that already exist + are interpreted as properties to be edited. A "None" value may specify a + property to be deleted. + + Properties must have a data type. There are lots of data types in IFCs, + not just simple unitless data types like integers, booleans, text, but + also distinguishing between types of text, like labels versus + descriptive text. There are also lots of unit-based data types like + areas, volumes, lengths, power, density, flow rates, pressure, etc. + + To ensure the appropriate data type is used for properties, a property + set template may be used. These can be seen as "property + specifications". A default selection is provided by buildingSMART, so + that all buildingSMART defined standard properties have exactly the same + data types and exactly the right property names without fear of invalid + data or typos. The built-in buildingSMART templates are always loaded. + However, you may also specify your own templates. If you try to add a + non-standard property that does not exist in either your own template or + in the built-in buildingSMART template, then you have the responsibility + to ensure that data types are always consistent and correct. + + :param pset: The IfcPropertySet to edit. + :type pset: ifcopenshell.entity_instance + :param name: A new name for the property set. If no name is specified, + the property set name is not changed. + :type name: str, optional + :param properties: A dictionary of properties. The keys must be a string + of the name of the property. The data type of the value will be + determined by the property set template. If no property set + template is found, the data types of the Python values will + influence the IFC data type of the property. String values will + become IfcLabel, float values will become IfcReal, booleans will + become IfcBoolean, and integers will become IfcInteger. If more + control is desired, you may explicitly specify IFC data objects + directly. Note that provided `properties` might be mutated in the process. + :type properties: dict + :param pset_template: If a property set template is provided, this will + be used to determine data types. If no user-defined template is + provided, the built-in buildingSMART templates will be loaded. + :type pset_template: ifcopenshell.entity_instance + :param should_purge: If left as False, properties set to None will be + left as None but not removed. If set to true, properties set to None + will actually be removed. + :type should_purge: bool, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a new wall type. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + + # This is a standard buildingSMART property set. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + + # In this scenario, we don't specify any pset_template because it is + # part of the built-in buildingSMART templates, and so the + # FireRating will automatically be an IfcLabel, and the thermal + # transmittance value will automatically be an + # IfcThermalTransmittanceMeasure. Neither of these properties exist + # yet, so they will be created. + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3}) + + # We can edit existing properties. In this case, "FireRating" is + # edited from "2HR" to "1HR". Combustible is new, and will be added. + # The existing "ThermalTransmittance" property will be left + # unchanged. + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"FireRating": "1HR", "Combustible": False}) + + # Setting to None will change the value but not delete the property. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None}) + + # If you actually want to delete the property, enable purging. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, + properties={"Combustible": None}, should_purge=True) + + # What if we wanted to manage our own properties? Let's create our + # own "Company Standard" property set templates. Notice how we + # prefix our property set with "Foo_", if our company name was "Foo" + # this would make sense. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar") + + # Let's imagine we want all model authors to specify two properties, + # one being a length measurement and another being a boolean. + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure") + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoB", primary_measure_type="IfcBoolean") + + # Now we can use our property set template to add our properties, + # and the data types will always match our template. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar") + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template) + + # Here's a third scenario where we want to add arbitrary properties + # that are not standardised by anything, not even our own custom + # templates. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset") + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={ + # Basic Python data types are mapped to a sensible default + "SomeLabel": "Foo", + "SomeNumber": 12.3, + # But we can always specify exactly what we're after too + "ExplicitLength": model.createIfcLengthMeasure(42.3) + }) + + # Editing existing properties will retain their current data types + # if possible. So this will still be a length measure. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "pset": pset, + "name": name, + "properties": properties or {}, + "pset_template": pset_template, + "should_purge": should_purge, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, pset=None, name=None, properties=None, pset_template=None, should_purge=False): - """Edits a property set and its properties - - At its simplest usage, this may be used to edit the name of a property - set. It may also be used to add, edit, or remove properties, either - arbitrarily or using a property set template. - - A list of properties are provided as a dictionary, where the keys are - property names, and values are property values. Keys that don't already - exist are interpreted as properties to be added. Keys that already exist - are interpreted as properties to be edited. A "None" value may specify a - property to be deleted. - - Properties must have a data type. There are lots of data types in IFCs, - not just simple unitless data types like integers, booleans, text, but - also distinguishing between types of text, like labels versus - descriptive text. There are also lots of unit-based data types like - areas, volumes, lengths, power, density, flow rates, pressure, etc. - - To ensure the appropriate data type is used for properties, a property - set template may be used. These can be seen as "property - specifications". A default selection is provided by buildingSMART, so - that all buildingSMART defined standard properties have exactly the same - data types and exactly the right property names without fear of invalid - data or typos. The built-in buildingSMART templates are always loaded. - However, you may also specify your own templates. If you try to add a - non-standard property that does not exist in either your own template or - in the built-in buildingSMART template, then you have the responsibility - to ensure that data types are always consistent and correct. - - :param pset: The IfcPropertySet to edit. - :type pset: ifcopenshell.entity_instance - :param name: A new name for the property set. If no name is specified, - the property set name is not changed. - :type name: str, optional - :param properties: A dictionary of properties. The keys must be a string - of the name of the property. The data type of the value will be - determined by the property set template. If no property set - template is found, the data types of the Python values will - influence the IFC data type of the property. String values will - become IfcLabel, float values will become IfcReal, booleans will - become IfcBoolean, and integers will become IfcInteger. If more - control is desired, you may explicitly specify IFC data objects - directly. Note that provided `properties` might be mutated in the process. - :type properties: dict - :param pset_template: If a property set template is provided, this will - be used to determine data types. If no user-defined template is - provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance - :param should_purge: If left as False, properties set to None will be - left as None but not removed. If set to true, properties set to None - will actually be removed. - :type should_purge: bool, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a new wall type. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - - # This is a standard buildingSMART property set. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - - # In this scenario, we don't specify any pset_template because it is - # part of the built-in buildingSMART templates, and so the - # FireRating will automatically be an IfcLabel, and the thermal - # transmittance value will automatically be an - # IfcThermalTransmittanceMeasure. Neither of these properties exist - # yet, so they will be created. - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3}) - - # We can edit existing properties. In this case, "FireRating" is - # edited from "2HR" to "1HR". Combustible is new, and will be added. - # The existing "ThermalTransmittance" property will be left - # unchanged. - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"FireRating": "1HR", "Combustible": False}) - - # Setting to None will change the value but not delete the property. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None}) - - # If you actually want to delete the property, enable purging. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, - properties={"Combustible": None}, should_purge=True) - - # What if we wanted to manage our own properties? Let's create our - # own "Company Standard" property set templates. Notice how we - # prefix our property set with "Foo_", if our company name was "Foo" - # this would make sense. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar") - - # Let's imagine we want all model authors to specify two properties, - # one being a length measurement and another being a boolean. - prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, - pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure") - prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, - pset_template=template, name="DemoB", primary_measure_type="IfcBoolean") - - # Now we can use our property set template to add our properties, - # and the data types will always match our template. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar") - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template) - - # Here's a third scenario where we want to add arbitrary properties - # that are not standardised by anything, not even our own custom - # templates. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset") - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={ - # Basic Python data types are mapped to a sensible default - "SomeLabel": "Foo", - "SomeNumber": 12.3, - # But we can always specify exactly what we're after too - "ExplicitLength": model.createIfcLengthMeasure(42.3) - }) - - # Editing existing properties will retain their current data types - # if possible. So this will still be a length measure. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3}) - """ - self.file = file - self.settings = { - "pset": pset, - "name": name, - "properties": properties or {}, - "pset_template": pset_template, - "should_purge": should_purge, - } - def execute(self): self.update_pset_name() self.load_pset_template() diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py index cd5a3bca05..ba5b7c93e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py @@ -20,97 +20,100 @@ import ifcopenshell import ifcopenshell.util.pset +def edit_qto(file, qto=None, name=None, properties=None, pset_template=None) -> None: + """Edits a quantity set and its quantities + + At its simplest usage, this may be used to edit the name of a quantity + set. It may also be used to add, edit, or remove quantities. + + See ifcopenshell.api.pset.edit_pset for documentation on how this is + intended to be used. + + One major difference is that quantities set to None are always purged. + It is not allowed to have None quantities in IFC. + + :param qto: The IfcElementQuantity to edit. + :type qto: ifcopenshell.entity_instance + :param name: A new name for the quantity set. If no name is specified, + the quantity set name is not changed. + :type name: str, optional + :param properties: A dictionary of properties. The keys must be a string + of the name of the quantity. The data type of the value will be + determined by the quantity set template. If no quantity set + template is found, the data types of the Python values will + influence the IFC data type of the quantity. String values will + become IfcLabel, float values will become IfcReal, booleans will + become IfcBoolean, and integers will become IfcInteger. If more + control is desired, you may explicitly specify IFC data objects + directly. + :type properties: dict + :param pset_template: If a quantity set template is provided, this will + be used to determine data types. If no user-defined template is + provided, the built-in buildingSMART templates will be loaded. + :type pset_template: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a new wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # This is a standard buildingSMART property set. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities") + + # In this scenario, we don't specify any pset_template because it is + # part of the built-in buildingSMART templates, and so the Length + # will automatically be an IfcLengthMeasure, and the NetVolume will + # automatically be an IfcVolumeMeasure. Neither of these properties + # exist yet, so they will be created. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2}) + + # Setting to None will delete the quantity. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None}) + + # What if we wanted to manage our own properties? Let's create our + # own "Company Standard" property set templates. Notice how we + # prefix our property set with "Foo_", if our company name was "Foo" + # this would make sense. In this example, we say that our template + # only applies to walls and is for quantities. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, + name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall") + + # Let's imagine we want all model authors to specify a length + # measurement for the portion of a wall that is overhanging. + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure") + + # Now we can use our property set template to add our properties, + # and the data types will always match our template. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall") + ifcopenshell.api.run("pset.edit_qto", model, + qto=qto, properties={"OverhangLength": 42.3}, pset_template=template) + + # Here's a third scenario where we want to add arbitrary quantities + # that are not standardised by anything, not even our own custom + # templates. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto") + ifcopenshell.api.run("pset.edit_qto", model, + qto=qto, properties={ + "SomeLength": model.createIfcLengthMeasure(42.3), + "SomeArea": model.createIfcAreaMeasure(21.0) + }) + + # Editing existing quantities will retain their current data types + # if possible. So this will still be a length measure. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template} + return usecase.execute() + + class Usecase: - def __init__(self, file, qto=None, name=None, properties=None, pset_template=None): - """Edits a quantity set and its quantities - - At its simplest usage, this may be used to edit the name of a quantity - set. It may also be used to add, edit, or remove quantities. - - See ifcopenshell.api.pset.edit_pset for documentation on how this is - intended to be used. - - One major difference is that quantities set to None are always purged. - It is not allowed to have None quantities in IFC. - - :param qto: The IfcElementQuantity to edit. - :type qto: ifcopenshell.entity_instance - :param name: A new name for the quantity set. If no name is specified, - the quantity set name is not changed. - :type name: str, optional - :param properties: A dictionary of properties. The keys must be a string - of the name of the quantity. The data type of the value will be - determined by the quantity set template. If no quantity set - template is found, the data types of the Python values will - influence the IFC data type of the quantity. String values will - become IfcLabel, float values will become IfcReal, booleans will - become IfcBoolean, and integers will become IfcInteger. If more - control is desired, you may explicitly specify IFC data objects - directly. - :type properties: dict - :param pset_template: If a quantity set template is provided, this will - be used to determine data types. If no user-defined template is - provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a new wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # This is a standard buildingSMART property set. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities") - - # In this scenario, we don't specify any pset_template because it is - # part of the built-in buildingSMART templates, and so the Length - # will automatically be an IfcLengthMeasure, and the NetVolume will - # automatically be an IfcVolumeMeasure. Neither of these properties - # exist yet, so they will be created. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2}) - - # Setting to None will delete the quantity. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None}) - - # What if we wanted to manage our own properties? Let's create our - # own "Company Standard" property set templates. Notice how we - # prefix our property set with "Foo_", if our company name was "Foo" - # this would make sense. In this example, we say that our template - # only applies to walls and is for quantities. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, - name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall") - - # Let's imagine we want all model authors to specify a length - # measurement for the portion of a wall that is overhanging. - prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure") - - # Now we can use our property set template to add our properties, - # and the data types will always match our template. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall") - ifcopenshell.api.run("pset.edit_qto", model, - qto=qto, properties={"OverhangLength": 42.3}, pset_template=template) - - # Here's a third scenario where we want to add arbitrary quantities - # that are not standardised by anything, not even our own custom - # templates. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto") - ifcopenshell.api.run("pset.edit_qto", model, - qto=qto, properties={ - "SomeLength": model.createIfcLengthMeasure(42.3), - "SomeArea": model.createIfcAreaMeasure(21.0) - }) - - # Editing existing quantities will retain their current data types - # if possible. So this will still be a length measure. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3}) - """ - self.file = file - self.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template} - def execute(self): self.qto_idx = 5 if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py index da77accbb6..50ef427bb4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py @@ -20,68 +20,65 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, product=None, pset=None): - """Removes a property set from a product +def remove_pset(file, product=None, pset=None) -> None: + """Removes a property set from a product - All properties that are part of this property set are also removed. + All properties that are part of this property set are also removed. - :param product: The IfcObject to remove the property set from. - :type product: ifcopenshell.entity_instance - :param pset: The IfcPropertySet or IfcElementQuantity to remove. - :type pset: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The IfcObject to remove the property set from. + :type product: ifcopenshell.entity_instance + :param pset: The IfcPropertySet or IfcElementQuantity to remove. + :type pset: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a new wall type with a property set. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + # Let's imagine we have a new wall type with a property set. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - # Remove it! - ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset) - """ - self.file = file - self.settings = {"product": product, "pset": pset} + # Remove it! + ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset) + """ + settings = {"product": product, "pset": pset} - def execute(self): - to_purge = [] - should_remove_pset = True - for inverse in self.file.get_inverse(self.settings["pset"]): - if inverse.is_a("IfcRelDefinesByProperties"): - if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1: - to_purge.append(inverse) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["product"]) - inverse.RelatedObjects = related_objects - should_remove_pset = False - if should_remove_pset: - properties = [] # Predefined psets have no properties - if self.settings["pset"].is_a("IfcPropertySet"): - properties = self.settings["pset"].HasProperties or [] - elif self.settings["pset"].is_a("IfcQuantitySet"): - properties = self.settings["pset"].Quantities or [] - elif self.settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"): - properties = self.settings["pset"].Properties or [] - for prop in properties: - if self.file.get_total_inverses(prop) != 1: - continue - if prop.is_a("IfcPropertyEnumeratedValue"): - enumeration = prop.EnumerationReference - if enumeration and self.file.get_total_inverses(enumeration) == 1: - self.file.remove(enumeration) - self.file.remove(prop) - # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory - history = getattr(self.settings["pset"], "OwnerHistory", None) - self.file.remove(self.settings["pset"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - for element in to_purge: - history = getattr(element, "OwnerHistory", None) - self.file.remove(element) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + to_purge = [] + should_remove_pset = True + for inverse in file.get_inverse(settings["pset"]): + if inverse.is_a("IfcRelDefinesByProperties"): + if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1: + to_purge.append(inverse) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["product"]) + inverse.RelatedObjects = related_objects + should_remove_pset = False + if should_remove_pset: + properties = [] # Predefined psets have no properties + if settings["pset"].is_a("IfcPropertySet"): + properties = settings["pset"].HasProperties or [] + elif settings["pset"].is_a("IfcQuantitySet"): + properties = settings["pset"].Quantities or [] + elif settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"): + properties = settings["pset"].Properties or [] + for prop in properties: + if file.get_total_inverses(prop) != 1: + continue + if prop.is_a("IfcPropertyEnumeratedValue"): + enumeration = prop.EnumerationReference + if enumeration and file.get_total_inverses(enumeration) == 1: + file.remove(enumeration) + file.remove(prop) + # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory + history = getattr(settings["pset"], "OwnerHistory", None) + file.remove(settings["pset"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + for element in to_purge: + history = getattr(element, "OwnerHistory", None) + file.remove(element) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py index e0caddbe3c..1c5963479c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_prop_template import add_prop_template +from .add_pset_template import add_pset_template +from .edit_prop_template import edit_prop_template +from .edit_pset_template import edit_pset_template +from .remove_prop_template import remove_prop_template +from .remove_pset_template import remove_pset_template diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py index 5a9dc42355..109c830c94 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py @@ -19,95 +19,91 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file, - pset_template=None, - name="NewProperty", - description=None, - template_type="P_SINGLEVALUE", - primary_measure_type="IfcLabel", - ): - """Adds new property templates to a property set template +def add_prop_template( + file, + pset_template=None, + name="NewProperty", + description=None, + template_type="P_SINGLEVALUE", + primary_measure_type="IfcLabel", +) -> None: + """Adds new property templates to a property set template - Assuming you first have a property set template, this allows you to add - templates for properties within that property set. A property template - lets you specify the name, description, and data type of a property. - When the template is provided to a model author, this gives them clear - instructions about the intention of the property and exactly which data - type to use. + Assuming you first have a property set template, this allows you to add + templates for properties within that property set. A property template + lets you specify the name, description, and data type of a property. + When the template is provided to a model author, this gives them clear + instructions about the intention of the property and exactly which data + type to use. - Types of properties and quantities include: + Types of properties and quantities include: - * P_SINGLEVALUE - a single value, the most common type of property. - * P_ENUMERATEDVALUE - the property value may one or more values chosen - from a preset list of values. - * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value. - * P_LISTVALUE - the property has a list of values. - * P_TABLEVALUE - the property has a table of values. - * P_REFERENCEVALUE - the property is a parametric reference to another - value. This is only for advanced users. - * Q_LENGTH - the quantity is a length. - * Q_AREA - the quantity is an area. - * Q_VOLUME - the quantity is a volume. - * Q_COUNT - the quantity is counting a item. - * Q_WEIGHT - the quantity is a weight. - * Q_TIME - the quantity is a time duration. + * P_SINGLEVALUE - a single value, the most common type of property. + * P_ENUMERATEDVALUE - the property value may one or more values chosen + from a preset list of values. + * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value. + * P_LISTVALUE - the property has a list of values. + * P_TABLEVALUE - the property has a table of values. + * P_REFERENCEVALUE - the property is a parametric reference to another + value. This is only for advanced users. + * Q_LENGTH - the quantity is a length. + * Q_AREA - the quantity is an area. + * Q_VOLUME - the quantity is a volume. + * Q_COUNT - the quantity is counting a item. + * Q_WEIGHT - the quantity is a weight. + * Q_TIME - the quantity is a time duration. - :param pset_template: The property set template to add the property - template to. - :type pset_template: ifcopenshell.entity_instance - :param name: The name of the property - :type name: str,optional - :param description: A few words describing what the property stores. - :type description: str,optional - :param primary_measure_type: The data type of the property. Consult the - IFC documentation for the full list of data types. - :param primary_measure_type: str,optional - :return: The newly created IfcSimplePropertyTemplate. - :rtype: ifcopenshell.entity_instance + :param pset_template: The property set template to add the property + template to. + :type pset_template: ifcopenshell.entity_instance + :param name: The name of the property + :type name: str,optional + :param description: A few words describing what the property stores. + :type description: str,optional + :param primary_measure_type: The data type of the property. Consult the + IFC documentation for the full list of data types. + :param primary_measure_type: str,optional + :return: The newly created IfcSimplePropertyTemplate. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple template that may be applied to all types - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a simple template that may be applied to all types + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's one example property - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="HighVoltage", description="Whether there is a risk of high voltage.", - primary_measure_type="IfcBoolean") + # Here's one example property + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") - # Here's another - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="ChemicalType", description="The class of chemical spillage.", - primary_measure_type="IfcLabel") - """ - self.file = file - self.settings = { - "pset_template": pset_template, - "name": name, - "description": description, - "template_type": template_type, - "primary_measure_type": primary_measure_type, + # Here's another + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="ChemicalType", description="The class of chemical spillage.", + primary_measure_type="IfcLabel") + """ + settings = { + "pset_template": pset_template, + "name": name, + "description": description, + "template_type": template_type, + "primary_measure_type": primary_measure_type, + } + + prop_template = file.create_entity( + "IfcSimplePropertyTemplate", + **{ + "GlobalId": ifcopenshell.guid.new(), + "Name": settings["name"], + "Description": settings["description"], + "PrimaryMeasureType": settings["primary_measure_type"], + "TemplateType": settings["template_type"], + "AccessState": "READWRITE", + "Enumerators": None, } - - def execute(self): - prop_template = self.file.create_entity( - "IfcSimplePropertyTemplate", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": self.settings["name"], - "Description": self.settings["description"], - "PrimaryMeasureType": self.settings["primary_measure_type"], - "TemplateType": self.settings["template_type"], - "AccessState": "READWRITE", - "Enumerators": None, - } - ) - has_property_templates = list(self.settings["pset_template"].HasPropertyTemplates or []) - has_property_templates.append(prop_template) - self.settings["pset_template"].HasPropertyTemplates = has_property_templates - return prop_template + ) + has_property_templates = list(settings["pset_template"].HasPropertyTemplates or []) + has_property_templates.append(prop_template) + settings["pset_template"].HasPropertyTemplates = has_property_templates + return prop_template diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py index 242f7b7510..9a22b6a97a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py @@ -19,95 +19,91 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file, - name="New_Pset", - template_type="PSET_TYPEDRIVENOVERRIDE", - applicable_entity="IfcObject,IfcTypeObject", - ): - """Adds a new property set template +def add_pset_template( + file, + name="New_Pset", + template_type="PSET_TYPEDRIVENOVERRIDE", + applicable_entity="IfcObject,IfcTypeObject", +) -> None: + """Adds a new property set template - This creates a new template for property sets. A template defines what - the name of the property set should be, what properties it can have, - what entities (e.g. wall) the property set can be assigned to, whether - it should be assigned at a type or occurrence level, the data types of - the properties, and descriptions of the properties. This template can - then be used as a project, company, or local government standard. + This creates a new template for property sets. A template defines what + the name of the property set should be, what properties it can have, + what entities (e.g. wall) the property set can be assigned to, whether + it should be assigned at a type or occurrence level, the data types of + the properties, and descriptions of the properties. This template can + then be used as a project, company, or local government standard. - buildingSMART itself ships a catalogue of property sets using these - templates, ensuring that internationally common properties (e.g. fire - rating of a wall) are all implemented exactly the same way across all - vendors and projects. Naturally, not everything can be standardised - internationally, so this allows you to create your own templates. + buildingSMART itself ships a catalogue of property sets using these + templates, ensuring that internationally common properties (e.g. fire + rating of a wall) are all implemented exactly the same way across all + vendors and projects. Naturally, not everything can be standardised + internationally, so this allows you to create your own templates. - You may either create a property template to store properties, or a - quantity template to store quantities. For convenience, we will always - call them "property templates" as they are conceptually very similar. + You may either create a property template to store properties, or a + quantity template to store quantities. For convenience, we will always + call them "property templates" as they are conceptually very similar. - This function only creates a template for the property set, not the - properties themselves within the property set. At this level, you are - allowed to define the name of the property set, whether it is type or - occurrence based, and which entities it applies to. + This function only creates a template for the property set, not the + properties themselves within the property set. At this level, you are + allowed to define the name of the property set, whether it is type or + occurrence based, and which entities it applies to. - See the documentation for IfcPropertySetTemplate for instructions on - the types of template type and list of applicable entities. + See the documentation for IfcPropertySetTemplate for instructions on + the types of template type and list of applicable entities. - The types of property set templates are: + The types of property set templates are: - * PSET_TYPEDRIVENONLY - assigned only to types - * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both, - the occurrence overrides the type. - * PSET_OCCURRENCEDRIVEN - assigned to occurrences only. - * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is - only recommended for advanced users. - * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities. - * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for - quantities. If both, the occurrence overrides the type. - * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for - quantities. + * PSET_TYPEDRIVENONLY - assigned only to types + * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both, + the occurrence overrides the type. + * PSET_OCCURRENCEDRIVEN - assigned to occurrences only. + * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is + only recommended for advanced users. + * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities. + * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for + quantities. If both, the occurrence overrides the type. + * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for + quantities. - By default, this creates a template that can be applied to types, but - overridden by occurrences, and is applicable to everything. + By default, this creates a template that can be applied to types, but + overridden by occurrences, and is applicable to everything. - :param name: The name of the property set - :type name: str,optional - :param template_type: Choose from one of PSET_TYPEDRIVENONLY, - PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN, - PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE, - QTO_OCCURRENCEDRIVEN, NOTDEFINED - :type template_type: str,optional - :param applicable_entity: The entity that this template is allowed to be - applied to. For example, IfcWall means that the property set may be - assigned to walls only. IfcTypeObject, the default, means that the - property set may be assigned to any type. - :type applicable_entity: str,optional - :return: The newly created IfcPropertySetTemplate - :rtype: ifcopenshell.entity_instance + :param name: The name of the property set + :type name: str,optional + :param template_type: Choose from one of PSET_TYPEDRIVENONLY, + PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN, + PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE, + QTO_OCCURRENCEDRIVEN, NOTDEFINED + :type template_type: str,optional + :param applicable_entity: The entity that this template is allowed to be + applied to. For example, IfcWall means that the property set may be + assigned to walls only. IfcTypeObject, the default, means that the + property set may be assigned to any type. + :type applicable_entity: str,optional + :return: The newly created IfcPropertySetTemplate + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple template that may be applied to all types - ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a simple template that may be applied to all types + ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Note that we aren't finished yet. Our property set template - # doesn't have any properties in it. Let's add a minimum of one - # property. - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="HighVoltage", description="Whether there is a risk of high voltage.", - primary_measure_type="IfcBoolean") - """ - self.file = file - self.settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity} + # Note that we aren't finished yet. Our property set template + # doesn't have any properties in it. Let's add a minimum of one + # property. + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") + """ + settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity} - def execute(self): - return self.file.create_entity( - "IfcPropertySetTemplate", - GlobalId=ifcopenshell.guid.new(), - Name=self.settings["name"], - TemplateType=self.settings["template_type"], - ApplicableEntity=self.settings["applicable_entity"], - ) + return file.create_entity( + "IfcPropertySetTemplate", + GlobalId=ifcopenshell.guid.new(), + Name=settings["name"], + TemplateType=settings["template_type"], + ApplicableEntity=settings["applicable_entity"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py index 6b2633992f..dcc6c9b3ae 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py @@ -17,36 +17,33 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, prop_template=None, attributes=None): - """Edits the attributes of an IfcSimplePropertyTemplate +def edit_prop_template(file, prop_template=None, attributes=None) -> None: + """Edits the attributes of an IfcSimplePropertyTemplate - For more information about the attributes and data types of an - IfcSimplePropertyTemplate, consult the IFC documentation. + For more information about the attributes and data types of an + IfcSimplePropertyTemplate, consult the IFC documentation. - :param prop_template: The IfcSimplePropertyTemplate entity you want to edit - :type prop_template: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param prop_template: The IfcSimplePropertyTemplate entity you want to edit + :type prop_template: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's a property with just default values. - prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + # Here's a property with just default values. + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - # Let's edit it to give the actual values we need. - ifcopenshell.api.run("pset_template.edit_prop_template", model, - prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) - """ - self.file = file - self.settings = {"prop_template": prop_template, "attributes": attributes or {}} + # Let's edit it to give the actual values we need. + ifcopenshell.api.run("pset_template.edit_prop_template", model, + prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) + """ + settings = {"prop_template": prop_template, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["prop_template"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["prop_template"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py index 303618f509..8a0581efdc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, pset_template=None, attributes=None): - """Edits the attributes of an IfcPropertySetTemplate +def edit_pset_template(file, pset_template=None, attributes=None) -> None: + """Edits the attributes of an IfcPropertySetTemplate - For more information about the attributes and data types of an - IfcPropertySetTemplate, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPropertySetTemplate, consult the IFC documentation. - :param pset_template: The IfcPropertySetTemplate entity you want to edit - :type pset_template: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param pset_template: The IfcPropertySetTemplate entity you want to edit + :type pset_template: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Whoops! We named it with a buildingSMART reserved "Pset_" prefix! - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors") + # Whoops! We named it with a buildingSMART reserved "Pset_" prefix! + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors") - # Let's fix it to prefix with our company code instead. - ifcopenshell.api.run("pset_template.edit_pset_template", model, - pset_template=template, attributes={"Name": "ABC_RiskFactors"}) - """ - self.file = file - self.settings = {"pset_template": pset_template, "attributes": attributes or {}} + # Let's fix it to prefix with our company code instead. + ifcopenshell.api.run("pset_template.edit_pset_template", model, + pset_template=template, attributes={"Name": "ABC_RiskFactors"}) + """ + settings = {"pset_template": pset_template, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["pset_template"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["pset_template"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py index 6479e6ffc2..7a247ac383 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py @@ -19,41 +19,38 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, prop_template=None): - """Removes a property template +def remove_prop_template(file, prop_template=None) -> None: + """Removes a property template - Note that a property set template should always have at least one - property template to be valid, so take care when removing property - templates. + Note that a property set template should always have at least one + property template to be valid, so take care when removing property + templates. - :param prop_template: The IfcSimplePropertyTemplate to remove. - :type prop_template: ifcopenshell.entity_instance - :return: None - :rtype: None + :param prop_template: The IfcSimplePropertyTemplate to remove. + :type prop_template: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's two propertes with just default values. - prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + # Here's two propertes with just default values. + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - # Let's remove the second one. - ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2) - """ - self.file = file - self.settings = {"prop_template": prop_template} + # Let's remove the second one. + ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2) + """ + settings = {"prop_template": prop_template} - def execute(self): - for inverse in self.file.get_inverse(self.settings["prop_template"]): - if len(inverse.HasPropertyTemplates) == 1: - inverse.HasPropertyTemplates = [] - else: - has_property_templates = list(inverse.HasPropertyTemplates) - has_property_templates.remove(self.settings["prop_template"]) - inverse.HasPropertyTemplates = has_property_templates - ifcopenshell.util.element.remove_deep(self.file, self.settings["prop_template"]) + for inverse in file.get_inverse(settings["prop_template"]): + if len(inverse.HasPropertyTemplates) == 1: + inverse.HasPropertyTemplates = [] + else: + has_property_templates = list(inverse.HasPropertyTemplates) + has_property_templates.remove(settings["prop_template"]) + inverse.HasPropertyTemplates = has_property_templates + ifcopenshell.util.element.remove_deep(file, settings["prop_template"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py index 4cb2c2695a..c567a55033 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py @@ -19,30 +19,27 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, pset_template=None): - """Removes a property set template +def remove_pset_template(file, pset_template=None) -> None: + """Removes a property set template - All property templates within the property set template are also removed - along with it. + All property templates within the property set template are also removed + along with it. - :param pset_template: The IfcPropertySetTemplate to remove. - :type pset_template: ifcopenshell.entity_instance - :return: None - :rtype: None + :param pset_template: The IfcPropertySetTemplate to remove. + :type pset_template: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a template. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a template. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Let's remove the template. - ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template) - """ - self.file = file - self.settings = {"pset_template": pset_template} + # Let's remove the template. + ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template) + """ + settings = {"pset_template": pset_template} - def execute(self): - ifcopenshell.util.element.remove_deep(self.file, self.settings["pset_template"]) + ifcopenshell.util.element.remove_deep(file, settings["pset_template"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py index e0caddbe3c..8fcff6a7fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_resource import add_resource +from .add_resource_quantity import add_resource_quantity +from .add_resource_time import add_resource_time +from .assign_resource import assign_resource +from .calculate_resource_usage import calculate_resource_usage +from .calculate_resource_work import calculate_resource_work +from .edit_resource import edit_resource +from .edit_resource_quantity import edit_resource_quantity +from .edit_resource_time import edit_resource_time +from .remove_resource import remove_resource +from .remove_resource_quantity import remove_resource_quantity +from .unassign_resource import unassign_resource diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py index 03a67ab648..e2a1dab308 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py @@ -19,93 +19,89 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, +def add_resource( + file, + parent_resource=None, + ifc_class="IfcCrewResource", + name=None, + predefined_type="NOTDEFINED", +) -> None: + """Add a new construction resource + + Construction resources may be managed and connected to cost schedules + and construction schedules. This allows calculations to be done on + resource utilisation, cost optimisation (e.g. labour rates), and + optioneering on build strategies. + + There are typically two types of resources. Crew resources are resources + where you manage your own crew and you have full control over the + equipment, labour, products, and materials used by your crew. + Alternatively, there are subcontractor resources, where you simply + delegate all the details to a subcontractor and it is not decomposed + into further levels of detail. + + This means when adding resources, you'd first either add a crew or + subcontract resource. If it is a crew resource, you'd then add child + resources to that crew, such as equipment (cranes, excavators, hoists, + etc), material (wood, concrete, etc), and labour (rigging crews, + formworkers, etc). + + :param parent_resource: If this is a child resource (typically to a crew + resource), then nominate the parent IfcConstructionResource here. + :type parent_resource: ifcopenshell.entity_instance + :param ifc_class: The class of resource chosen from + IfcConstructionEquipmentResource, IfcConstructionMaterialResource, + IfcConstructionProductResource, IfcCrewResource, IfcLaborResource, + or IfcSubContractResource. + :type ifc_class: str,optional + :param name: The name of the resource + :type name: str,optional + :param predefined_type: Consult the IFC documentation for the valid + predefined types for each type of resource class. + :type predefined_type: str,optional + :return: The newly created resource depending on the nominated IFC + class. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource") + """ + settings = { + "parent_resource": parent_resource, + "ifc_class": ifc_class, + "name": name, + "predefined_type": predefined_type, + } + + resource = ifcopenshell.api.run( + "root.create_entity", file, - parent_resource=None, - ifc_class="IfcCrewResource", - name=None, - predefined_type="NOTDEFINED", - ): - """Add a new construction resource - - Construction resources may be managed and connected to cost schedules - and construction schedules. This allows calculations to be done on - resource utilisation, cost optimisation (e.g. labour rates), and - optioneering on build strategies. - - There are typically two types of resources. Crew resources are resources - where you manage your own crew and you have full control over the - equipment, labour, products, and materials used by your crew. - Alternatively, there are subcontractor resources, where you simply - delegate all the details to a subcontractor and it is not decomposed - into further levels of detail. - - This means when adding resources, you'd first either add a crew or - subcontract resource. If it is a crew resource, you'd then add child - resources to that crew, such as equipment (cranes, excavators, hoists, - etc), material (wood, concrete, etc), and labour (rigging crews, - formworkers, etc). - - :param parent_resource: If this is a child resource (typically to a crew - resource), then nominate the parent IfcConstructionResource here. - :type parent_resource: ifcopenshell.entity_instance - :param ifc_class: The class of resource chosen from - IfcConstructionEquipmentResource, IfcConstructionMaterialResource, - IfcConstructionProductResource, IfcCrewResource, IfcLaborResource, - or IfcSubContractResource. - :type ifc_class: str,optional - :param name: The name of the resource - :type name: str,optional - :param predefined_type: Consult the IFC documentation for the valid - predefined types for each type of resource class. - :type predefined_type: str,optional - :return: The newly created resource depending on the nominated IFC - class. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource") - """ - self.file = file - self.settings = { - "parent_resource": parent_resource, - "ifc_class": ifc_class, - "name": name, - "predefined_type": predefined_type, - } - - def execute(self): - resource = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class=self.settings["ifc_class"], - predefined_type=self.settings["predefined_type"], - name=self.settings["name"] or "Unnamed", + ifc_class=settings["ifc_class"], + predefined_type=settings["predefined_type"], + name=settings["name"] or "Unnamed", + ) + # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ? + # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550 + if settings["parent_resource"]: + ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[resource], + relating_object=settings["parent_resource"], ) - # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ? - # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550 - if self.settings["parent_resource"]: - ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[resource], - relating_object=self.settings["parent_resource"], - ) - else: - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[resource], - relating_context=context, - ) - return resource + else: + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[resource], + relating_context=context, + ) + return resource diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py index 4e5ef0c0a0..6600a06ae2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -19,58 +19,55 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None, ifc_class="IfcQuantityCount"): - """Adds a quantity to a resource +def add_resource_quantity(file, resource=None, ifc_class="IfcQuantityCount") -> None: + """Adds a quantity to a resource - The quantity of a resource represents the "unit quantity" of that - resource. For example, labour might be hired on a daily basis (8 hours). - There are different types of quantities (e.g. volume, count, or time). - Which quantity is used depends on the type of resource. Material - resources may be quantified in terms of length, area, volume, or weight. - Equipment and labour resources are quantified in terms of time. Products - resources are quantified in terms of counts. + The quantity of a resource represents the "unit quantity" of that + resource. For example, labour might be hired on a daily basis (8 hours). + There are different types of quantities (e.g. volume, count, or time). + Which quantity is used depends on the type of resource. Material + resources may be quantified in terms of length, area, volume, or weight. + Equipment and labour resources are quantified in terms of time. Products + resources are quantified in terms of counts. - This base quantity is then used in other calculations. + This base quantity is then used in other calculations. - :param resource: The IfcConstructionResource to add a quantity to. - :type resource: ifcopenshell.entity_instance - :param ifc_class: The type of quantity to add, chosen from - IfcQuantityArea (for material), IfcQuantityCount (for products), - IfcQuantityLength (for material), IfcQuantityTime (for equipment or - labour), IfcQuantityVolume (for material), and IfcQuantityWeight - (for material). - :type ifc_class: str,optional - :return: The newly created quantity depending on the IFC class - :rtype: ifcopenshell.entity_instance + :param resource: The IfcConstructionResource to add a quantity to. + :type resource: ifcopenshell.entity_instance + :param ifc_class: The type of quantity to add, chosen from + IfcQuantityArea (for material), IfcQuantityCount (for products), + IfcQuantityLength (for material), IfcQuantityTime (for equipment or + labour), IfcQuantityVolume (for material), and IfcQuantityWeight + (for material). + :type ifc_class: str,optional + :return: The newly created quantity depending on the IFC class + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) - """ - self.file = file - self.settings = {"resource": resource, "ifc_class": ifc_class} + # Store the time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) + """ + settings = {"resource": resource, "ifc_class": ifc_class} - def execute(self): - quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") - quantity[3] = 0.0 - old_quantity = self.settings["resource"].BaseQuantity - self.settings["resource"].BaseQuantity = quantity - if old_quantity: - ifcopenshell.util.element.remove_deep(self.file, old_quantity) - return quantity + quantity = file.create_entity(settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + old_quantity = settings["resource"].BaseQuantity + settings["resource"].BaseQuantity = quantity + if old_quantity: + ifcopenshell.util.element.remove_deep(file, old_quantity) + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py index 8627e319a7..3066441330 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py @@ -19,50 +19,47 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, resource=None): - """Adds the time that a resource is used for +def add_resource_time(file, resource=None) -> None: + """Adds the time that a resource is used for - For labour and equipment resources, the total duration that the resource - is used for may be stored. This may either be input manually or - calculated parametrically. This is known as the resource time, and may - be used to calculate other parameters like resource utilisation. + For labour and equipment resources, the total duration that the resource + is used for may be stored. This may either be input manually or + calculated parametrically. This is known as the resource time, and may + be used to calculate other parameters like resource utilisation. - :param resource: The IfcConstructionResource to record time for. - :type resource: ifcopenshell.entity_instance - :return: The newly created IfcResourceTime - :rtype: ifcopenshell.entity_instance + :param resource: The IfcConstructionResource to record time for. + :type resource: ifcopenshell.entity_instance + :return: The newly created IfcResourceTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) - # Let's imagine we've used the resource for 2 days. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "PT16H"}) - """ - self.file = file - self.settings = { - "resource": resource, - } + # Let's imagine we've used the resource for 2 days. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "PT16H"}) + """ + settings = { + "resource": resource, + } - def execute(self): - resource_time = self.file.create_entity("IfcResourceTime") - self.settings["resource"].Usage = resource_time - return resource_time + resource_time = file.create_entity("IfcResourceTime") + settings["resource"].Usage = resource_time + return resource_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py index f71ec00260..44d856ef0f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py @@ -20,101 +20,93 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_resource=None, related_object=None): - """Assigns a resource to an object +def assign_resource(file, relating_resource=None, related_object=None) -> None: + """Assigns a resource to an object - Two types of objects are typically assigned to resources: products and - actors. + Two types of objects are typically assigned to resources: products and + actors. - If a product is assigned to a resource, that means that the product - represents the resource on site. This may be represented via material - handling zones on a construction site, or equipment like cranes and - their physical locations. + If a product is assigned to a resource, that means that the product + represents the resource on site. This may be represented via material + handling zones on a construction site, or equipment like cranes and + their physical locations. - If an actor is assigned to a resource, that means that the actor (person - or organisation) is the actor consuming the resource (e.g. if the - resource is material or equipment) or the actor performing the work - (e.g. if the resource is a labour resource). + If an actor is assigned to a resource, that means that the actor (person + or organisation) is the actor consuming the resource (e.g. if the + resource is material or equipment) or the actor performing the work + (e.g. if the resource is a labour resource). - :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance - :param related_object: The IfcProduct or IfcActor to assign to the - object. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance + :param relating_resource: The IfcResource to assign the object to. + :type relating_resource: ifcopenshell.entity_instance + :param related_object: The IfcProduct or IfcActor to assign to the + object. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToResource + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some a tower crane to our crew. - crane = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") + # Add some a tower crane to our crew. + crane = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") - # Our tower crane will be placed via this physical product. - product = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") + # Our tower crane will be placed via this physical product. + product = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") - # Let's place our crane at some X, Y coordinates. - matrix = numpy.eye(4) - matrix[0][3], matrix[1][3] = 3.0, 4.0 - ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix) + # Let's place our crane at some X, Y coordinates. + matrix = numpy.eye(4) + matrix[0][3], matrix[1][3] = 3.0, 4.0 + ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix) - # Let's assign our crane to the resource. The crane now represents - # the resource. - ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product) + # Let's assign our crane to the resource. The crane now represents + # the resource. + ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product) - # Setup an organisation actor who will operate the crane - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="UCO", name="Unionised Crane Operators Pty Ltd") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW") - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Setup an organisation actor who will operate the crane + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="UCO", name="Unionised Crane Operators Pty Ltd") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW") + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # This means that UCO is now our crane operator. - ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor) - """ - self.file = file - self.settings = { - "relating_resource": relating_resource, - "related_object": related_object, - } + # This means that UCO is now our crane operator. + ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor) + """ + settings = { + "relating_resource": relating_resource, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfclRelAssignsToResource") - and assignment.RelatingResource - == self.settings["relating_resource"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if ( + assignment.is_a("IfclRelAssignsToResource") + and assignment.RelatingResource == settings["relating_resource"] + ): + return - resource_of = None - if self.settings["relating_resource"].ResourceOf: - resource_of = self.settings["relating_resource"].ResourceOf[0] + resource_of = None + if settings["relating_resource"].ResourceOf: + resource_of = settings["relating_resource"].ResourceOf[0] - if resource_of: - related_objects = list(resource_of.RelatedObjects) - related_objects.append(self.settings["related_object"]) - resource_of.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": resource_of} - ) - else: - resource_of = self.file.create_entity( - "IfcRelAssignsToResource", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingResource": self.settings["relating_resource"], - } - ) - return resource_of + if resource_of: + related_objects = list(resource_of.RelatedObjects) + related_objects.append(settings["related_object"]) + resource_of.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": resource_of}) + else: + resource_of = file.create_entity( + "IfcRelAssignsToResource", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingResource": settings["relating_resource"], + } + ) + return resource_of diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py index 08602c9f0f..fc63b2d9d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py @@ -23,42 +23,29 @@ import ifcopenshell.util.element import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, resource=None): - """Calculates the number of resources required to perform scheduled work on a task. - """ - self.file = file - self.settings = {"resource": resource} +def calculate_resource_usage(file, resource=None) -> None: + """Calculates the number of resources required to perform scheduled work on a task.""" + settings = {"resource": resource} - def execute(self): - if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleUsage"): - return - if ( - not self.settings["resource"].Usage - or not self.settings["resource"].Usage.ScheduleWork - ): - return + if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleUsage"): + return + if not settings["resource"].Usage or not settings["resource"].Usage.ScheduleWork: + return - task = ifcopenshell.util.resource.get_task_assignments( - self.settings["resource"] - ) - if not task or not task.TaskTime: - return + task = ifcopenshell.util.resource.get_task_assignments(settings["resource"]) + if not task or not task.TaskTime: + return - if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME": - hours_per_day = 8 - else: - hours_per_day = 24 + if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME": + hours_per_day = 8 + else: + hours_per_day = 24 - task_duration = ifcopenshell.util.date.ifc2datetime( - task.TaskTime.ScheduleDuration - ) - seconds = task_duration.days * hours_per_day * 60 * 60 - seconds += task_duration.seconds + task_duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration) + seconds = task_duration.days * hours_per_day * 60 * 60 + seconds += task_duration.seconds - person_hours = ifcopenshell.util.date.ifc2datetime( - self.settings["resource"].Usage.ScheduleWork - ) + person_hours = ifcopenshell.util.date.ifc2datetime(settings["resource"].Usage.ScheduleWork) - required_resources = person_hours.total_seconds() / seconds - self.settings["resource"].Usage.ScheduleUsage = float(required_resources) + required_resources = person_hours.total_seconds() / seconds + settings["resource"].Usage.ScheduleUsage = float(required_resources) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py index 746f0d88c0..dd5621d386 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py @@ -23,52 +23,49 @@ import ifcopenshell.util.element import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, resource=None): - """Calculates the work that a resource is used for +def calculate_resource_work(file, resource=None) -> None: + """Calculates the work that a resource is used for - This is an unofficial parametric calculation that may be done on a - resource based on careful analysis of the relationships between the - costing, scheduling, and resource domains in IFC. + This is an unofficial parametric calculation that may be done on a + resource based on careful analysis of the relationships between the + costing, scheduling, and resource domains in IFC. - A resource may store a productivity rate in a property set called - EPset_Productivity. This stores three properties: + A resource may store a productivity rate in a property set called + EPset_Productivity. This stores three properties: - * BaseQuantityConsumed - a duration that the resource is consumed for. - * BaseQuantityProducedName - what quantity the resource can produce, - such as area or volume. - * BaseQuantityProducedValue - what value of that quantity the resource - can produce during that duration. + * BaseQuantityConsumed - a duration that the resource is consumed for. + * BaseQuantityProducedName - what quantity the resource can produce, + such as area or volume. + * BaseQuantityProducedValue - what value of that quantity the resource + can produce during that duration. - For example, a labour or equipment resource might produce 100m3 of - NetVolume every day (i.e. 8 hours are consumed). + For example, a labour or equipment resource might produce 100m3 of + NetVolume every day (i.e. 8 hours are consumed). - Then, if a resource is assigned to a construction task, and that - construction task is assigned to concrete slabs totalling 200m3, we can - calculate that the resource consumes 16 hours of work. + Then, if a resource is assigned to a construction task, and that + construction task is assigned to concrete slabs totalling 200m3, we can + calculate that the resource consumes 16 hours of work. - This calculated work is stored against the resource as scheduled work - under the resource time data. + This calculated work is stored against the resource as scheduled work + under the resource time data. - :param resource: The IfcConstructionResource that you want to calculate - the work performed. - :type resource: ifcopenshell.entity_instance - :return None: - :rtype: None: - """ - self.file = file - self.settings = {"resource": resource} + :param resource: The IfcConstructionResource that you want to calculate + the work performed. + :type resource: ifcopenshell.entity_instance + :return None: + :rtype: None: + """ + settings = {"resource": resource} - def execute(self): - if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleWork"): - return - amount_worked = ifcopenshell.util.resource.get_resource_required_work(self.settings["resource"]) - if not amount_worked: - return - if not self.settings["resource"].Usage: - ifcopenshell.api.run( - "resource.add_resource_time", - self.file, - resource=self.settings["resource"], - ) - self.settings["resource"].Usage.ScheduleWork = amount_worked + if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleWork"): + return + amount_worked = ifcopenshell.util.resource.get_resource_required_work(settings["resource"]) + if not amount_worked: + return + if not settings["resource"].Usage: + ifcopenshell.api.run( + "resource.add_resource_time", + file, + resource=settings["resource"], + ) + settings["resource"].Usage.ScheduleWork = amount_worked diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py index c28f4c0661..2ab8ac669a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, resource=None, attributes=None): - """Edits the attributes of an IfcResource +def edit_resource(file, resource=None, attributes=None) -> None: + """Edits the attributes of an IfcResource - For more information about the attributes and data types of an - IfcResource, consult the IFC documentation. + For more information about the attributes and data types of an + IfcResource, consult the IFC documentation. - :param resource: The IfcResource entity you want to edit - :type resource: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param resource: The IfcResource entity you want to edit + :type resource: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Change the name of the resource to "Zone A Crew" - ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"resource": resource, "attributes": attributes or {}} + # Change the name of the resource to "Zone A Crew" + ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"}) + """ + settings = {"resource": resource, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["resource"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["resource"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py index 0785caa02e..b4d016c7ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -17,45 +17,42 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, physical_quantity=None, attributes=None): - """Edits the attributes of an IFC quantity +def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> None: + """Edits the attributes of an IFC quantity - For more information about the attributes and data types of an - IfC quantity, consult the IFC documentation. + For more information about the attributes and data types of an + IfC quantity, consult the IFC documentation. - :param physical_quantity: The IfC quantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param physical_quantity: The IfC quantity entity you want to edit + :type physical_quantity: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=time, attributes={"TimeValue": 8.0}) - """ - self.file = file - self.settings = { - "physical_quantity": physical_quantity, - "attributes": attributes or {}, - } + # Store the time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=time, attributes={"TimeValue": 8.0}) + """ + settings = { + "physical_quantity": physical_quantity, + "attributes": attributes or {}, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["physical_quantity"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py index c9db827a89..9ec41a60c5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py @@ -20,47 +20,50 @@ import datetime import ifcopenshell +def edit_resource_time(file, resource_time=None, attributes=None) -> None: + """Edits the attributes of an IfcResourceTime + + For more information about the attributes and data types of an + IfcResourceTime, consult the IFC documentation. + + :param resource_time: The IfcResourceTime entity you want to edit + :type resource_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") + + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") + + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=time, attributes={"TimeValue": 8.0}) + + # Let's imagine we've used the resource for 2 days. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "P16H"}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"resource_time": resource_time, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, resource_time=None, attributes=None): - """Edits the attributes of an IfcResourceTime - - For more information about the attributes and data types of an - IfcResourceTime, consult the IFC documentation. - - :param resource_time: The IfcResourceTime entity you want to edit - :type resource_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") - - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") - - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=time, attributes={"TimeValue": 8.0}) - - # Let's imagine we've used the resource for 2 days. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "P16H"}) - """ - self.file = file - self.settings = {"resource_time": resource_time, "attributes": attributes or {}} - def execute(self): self.resource = self.get_resource() @@ -70,43 +73,25 @@ class Usecase: and "ScheduleFinish" in self.settings["attributes"].keys() ): del self.settings["attributes"]["ScheduleFinish"] - if ( - self.settings["attributes"].get("ActualWork", None) - and "ActualFinish" in self.settings["attributes"].keys() - ): + if self.settings["attributes"].get("ActualWork", None) and "ActualFinish" in self.settings["attributes"].keys(): del self.settings["attributes"]["ActualFinish"] for name, value in self.settings["attributes"].items(): - metrics = ifcopenshell.util.constraint.get_metric_constraints( - self.resource, "Usage." + name - ) + metrics = ifcopenshell.util.constraint.get_metric_constraints(self.resource, "Usage." + name) if metrics and ifcopenshell.util.constraint.is_hard_constraint(metrics[0]): continue if value: if "Start" in name or "Finish" in name or name == "StatusTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif ( - name == "ScheduleWork" - or name == "ActualWork" - or name == "RemainingTime" - ): + elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") setattr(self.settings["resource_time"], name, value) - if ( - name == "ScheduleUsage" - and ifcopenshell.util.constraint.get_metric_constraints( - self.resource, "Usage.ScheduleWork" - ) + if name == "ScheduleUsage" and ifcopenshell.util.constraint.get_metric_constraints( + self.resource, "Usage.ScheduleWork" ): task = ifcopenshell.util.resource.get_task_assignments(self.resource) if task: - ifcopenshell.api.run( - "sequence.calculate_task_duration", self.file, task=task - ) + ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=task) def get_resource(self): - return [ - e - for e in self.file.get_inverse(self.settings["resource_time"]) - if e.is_a("IfcResource") - ][0] + return [e for e in self.file.get_inverse(self.settings["resource_time"]) if e.is_a("IfcResource")][0] diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py index db8153a3e0..cfbdd25fd9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py @@ -21,71 +21,68 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None): - """Removes a resource and all relationships +def remove_resource(file, resource=None) -> None: + """Removes a resource and all relationships - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Fire our crew - ifcopenshell.api.run("resource.remove_resource", model, resource=crew) - """ - self.file = file - self.settings = {"resource": resource} + # Fire our crew + ifcopenshell.api.run("resource.remove_resource", model, resource=crew) + """ + settings = {"resource": resource} - def execute(self): - # TODO: review deep purge - for inverse in self.file.get_inverse(self.settings["resource"]): - if inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["resource"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run( - "resource.remove_resource", - self.file, - resource=related_object, - ) - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToControl"): - if len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["resource"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToResource"): - if inverse.RelatingResource == self.settings["resource"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run( - "resource.unassign_resource", - self.file, - related_object=related_object, - resource=self.settings["resource"], - ) - elif inverse.RelatedObjects == tuple(self.settings["resource"]): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - if self.settings["resource"].Usage: - self.file.remove(self.settings["resource"].Usage) - if self.settings["resource"].BaseQuantity: - ifcopenshell.api.run( - "resource.remove_resource_quantity", - self.file, - resource=self.settings["resource"], - ) - history = self.settings["resource"].OwnerHistory - self.file.remove(self.settings["resource"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: review deep purge + for inverse in file.get_inverse(settings["resource"]): + if inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["resource"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run( + "resource.remove_resource", + file, + resource=related_object, + ) + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToControl"): + if len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["resource"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToResource"): + if inverse.RelatingResource == settings["resource"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run( + "resource.unassign_resource", + file, + related_object=related_object, + resource=settings["resource"], + ) + elif inverse.RelatedObjects == tuple(settings["resource"]): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + if settings["resource"].Usage: + file.remove(settings["resource"].Usage) + if settings["resource"].BaseQuantity: + ifcopenshell.api.run( + "resource.remove_resource_quantity", + file, + resource=settings["resource"], + ) + history = settings["resource"].OwnerHistory + file.remove(settings["resource"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py index afafa9a193..221d94c5a6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py @@ -19,34 +19,31 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None): - """Removes the base quantity of a resource +def remove_resource_quantity(file, resource=None) -> None: + """Removes the base quantity of a resource - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Let's say we only want to store the resource but no quantities, - # let's clean up our mess and remove the quantity. - ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour) - """ - self.file = file - self.settings = {"resource": resource} + # Let's say we only want to store the resource but no quantities, + # let's clean up our mess and remove the quantity. + ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour) + """ + settings = {"resource": resource} - def execute(self): - old_quantity = self.settings["resource"].BaseQuantity - self.settings["resource"].BaseQuantity = None - if old_quantity: - ifcopenshell.util.element.remove_deep(self.file, old_quantity) + old_quantity = settings["resource"].BaseQuantity + settings["resource"].BaseQuantity = None + if old_quantity: + ifcopenshell.util.element.remove_deep(file, old_quantity) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py index ceed0dbf2a..7b1a59f519 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py @@ -21,65 +21,57 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_resource=None, related_object=None): - """Removes the relationship between a resource and object +def unassign_resource(file, relating_resource=None, related_object=None) -> None: + """Removes the relationship between a resource and object - :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance - :param related_object: The IfcProduct or IfcActor to assign to the - object. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance + :param relating_resource: The IfcResource to assign the object to. + :type relating_resource: ifcopenshell.entity_instance + :param related_object: The IfcProduct or IfcActor to assign to the + object. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToResource + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some a tower crane to our crew. - crane = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") + # Add some a tower crane to our crew. + crane = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") - # Our tower crane will be placed via this physical product. - product = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") + # Our tower crane will be placed via this physical product. + product = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") - # Let's assign our crane to the resource. The crane now represents - # the resource. - ifcopenshell.api.run("resource.assign_resource", model, - relating_resource=crane, related_object=product) + # Let's assign our crane to the resource. The crane now represents + # the resource. + ifcopenshell.api.run("resource.assign_resource", model, + relating_resource=crane, related_object=product) - # Undo it. - ifcopenshell.api.run("resource.unassign_resource", model, - relating_resource=crane, related_object=product) - """ - self.file = file - self.settings = { - "relating_resource": relating_resource, - "related_object": related_object, - } + # Undo it. + ifcopenshell.api.run("resource.unassign_resource", model, + relating_resource=crane, related_object=product) + """ + settings = { + "relating_resource": relating_resource, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if ( - not rel.is_a("IfcRelAssignsToResource") - or rel.RelatingResource != self.settings["relating_resource"] - ): - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": rel} - ) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != settings["relating_resource"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py index e0caddbe3c..309f87cfff 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .copy_class import copy_class +from .create_entity import create_entity +from .reassign_class import reassign_class +from .remove_product import remove_product diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index 389930d409..976010c3c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -21,52 +21,55 @@ import ifcopenshell.util.system import ifcopenshell.util.element +def copy_class(file, product=None) -> None: + """Copies a product + + The following relationships are also duplicated: + + * The copy will have the same object placement coordinates as the + original. + * The copy will have duplicated property sets, properties, and quantities + * The copy will have all nested distribution ports copied too + * The copy will be part of the same aggregate + * The copy will be contained in the same spatial structure + * The copy, if it is an occurrence, will have the same type + * Voids are duplicated too + * The copy will have the same material as the original. Parametric + material set usages will be copied. + * The copy will be part of the same groups as the original. + + Be warned that: + + * Representations are _not_ copied. Copying representations is an + expensive operation so for now the user is responsible for handling + representations. + * Filled voids are not copied, as there is no guarantee that the filling + will also be copied. + * Path connectivity is not copied, as there is no guarantee that the + connections are still valid. + + :param product: The IfcProduct to copy. + :type param: ifcopenshell.entity_instance + :return: The copied product + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # And now we have two + wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": product} + return usecase.execute() + + class Usecase: - def __init__(self, file, product=None): - """Copies a product - - The following relationships are also duplicated: - - * The copy will have the same object placement coordinates as the - original. - * The copy will have duplicated property sets, properties, and quantities - * The copy will have all nested distribution ports copied too - * The copy will be part of the same aggregate - * The copy will be contained in the same spatial structure - * The copy, if it is an occurrence, will have the same type - * Voids are duplicated too - * The copy will have the same material as the original. Parametric - material set usages will be copied. - * The copy will be part of the same groups as the original. - - Be warned that: - - * Representations are _not_ copied. Copying representations is an - expensive operation so for now the user is responsible for handling - representations. - * Filled voids are not copied, as there is no guarantee that the filling - will also be copied. - * Path connectivity is not copied, as there is no guarantee that the - connections are still valid. - - :param product: The IfcProduct to copy. - :type param: ifcopenshell.entity_instance - :return: The copied product - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # And now we have two - wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall) - """ - self.file = file - self.settings = {"product": product} - def execute(self): result = ifcopenshell.util.element.copy(self.file, self.settings["product"]) self.copy_direct_attributes(result) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py index 7619eec067..5bb64d411a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py @@ -21,63 +21,65 @@ import ifcopenshell.api from typing import Optional +def create_entity( + file: ifcopenshell.file, + ifc_class: str = "IfcBuildingElementProxy", + predefined_type: Optional[str] = None, + name: Optional[str] = None, +) -> ifcopenshell.entity_instance: + """Create a new rooted product + + This is a critical function used to create almost any rooted product or + product type. If you want to create walls, spaces, buildings, wall + types, and so on, use this function. + + Just specify the class you want to create, as well as the predefined + type and name. It will handle the storage of the predefined type and + check whether the predefined type is built-in or custom. It will also + generate a valid GlobalId and store ownership history. It will also + handle some edge cases for default validity where users might forget to + populate some mandatory attributes. For example, doors must define an + operation type but many people forget. + + :param ifc_class: Any rooted IFC class. + :type ifc_class: str,optional + :param predefined_type: Any built-in or user-defined predefined type that + is applicable to that IFC class. For user-defined predefined types + just enter in any value and the API will handle it automatically. + :type predefined_type: str,optional + :param name: The name of the new element. + :type name: str,optional + :return: The newly created element based on the specified IFC class. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a project. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + + # We have a building. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + + # We have a wall. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # We have a wall type. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "ifc_class": ifc_class, + "predefined_type": predefined_type, + "name": name, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - ifc_class: str = "IfcBuildingElementProxy", - predefined_type: Optional[str] = None, - name: Optional[str] = None, - ): - """Create a new rooted product - - This is a critical function used to create almost any rooted product or - product type. If you want to create walls, spaces, buildings, wall - types, and so on, use this function. - - Just specify the class you want to create, as well as the predefined - type and name. It will handle the storage of the predefined type and - check whether the predefined type is built-in or custom. It will also - generate a valid GlobalId and store ownership history. It will also - handle some edge cases for default validity where users might forget to - populate some mandatory attributes. For example, doors must define an - operation type but many people forget. - - :param ifc_class: Any rooted IFC class. - :type ifc_class: str,optional - :param predefined_type: Any built-in or user-defined predefined type that - is applicable to that IFC class. For user-defined predefined types - just enter in any value and the API will handle it automatically. - :type predefined_type: str,optional - :param name: The name of the new element. - :type name: str,optional - :return: The newly created element based on the specified IFC class. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a project. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - - # We have a building. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - - # We have a wall. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # We have a wall type. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - """ - self.file = file - self.settings = { - "ifc_class": ifc_class, - "predefined_type": predefined_type, - "name": name, - } - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): element = self.file.create_entity( self.settings["ifc_class"], **{ diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py index 1035a6b17c..c124786a83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -22,61 +22,63 @@ import ifcopenshell.util.schema import ifcopenshell.util.element +def reassign_class( + file, + product=None, + ifc_class="IfcBuildingElementProxy", + predefined_type=None, +) -> None: + """Changes the class of a product + + If you ever created a wall then realised it's meant to be something + else, this function lets you change the IFC class whilst retaining all + other geometry and relationships. + + This is especially useful when dealing with poorly classified data from + proprietary software with limited IFC capabilities. + + If you are reassigning a type, the occurrence classes are also + reassigned to maintain validity. + + Vice versa, if you are reassigning an occurrence, the type is also + reassigned in IFC4 and up. In IFC2X3, this may not occur if the type + cannot be unambiguously derived, so you are required to manually check + this. + + :param product: The IfcProduct that you want to change the class of. + :type product: ifcopenshell.entity_instance + :param ifc_class: The new IFC class you want to change it to. + :type ifc_class: str,optional + :param predefined_type: In case you want to change the predefined type + too. User defined types are also allowed, just type what you want. + :type predefined_type: str,optional + :return: The newly modified product. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Oh, did I say wall? I meant slab. + slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab") + + # Warning: this will crash since wall doesn't exist any more. + print(wall) # Kaboom. + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "product": product, + "ifc_class": ifc_class, + "predefined_type": predefined_type, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - product=None, - ifc_class="IfcBuildingElementProxy", - predefined_type=None, - ): - """Changes the class of a product - - If you ever created a wall then realised it's meant to be something - else, this function lets you change the IFC class whilst retaining all - other geometry and relationships. - - This is especially useful when dealing with poorly classified data from - proprietary software with limited IFC capabilities. - - If you are reassigning a type, the occurrence classes are also - reassigned to maintain validity. - - Vice versa, if you are reassigning an occurrence, the type is also - reassigned in IFC4 and up. In IFC2X3, this may not occur if the type - cannot be unambiguously derived, so you are required to manually check - this. - - :param product: The IfcProduct that you want to change the class of. - :type product: ifcopenshell.entity_instance - :param ifc_class: The new IFC class you want to change it to. - :type ifc_class: str,optional - :param predefined_type: In case you want to change the predefined type - too. User defined types are also allowed, just type what you want. - :type predefined_type: str,optional - :return: The newly modified product. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Oh, did I say wall? I meant slab. - slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab") - - # Warning: this will crash since wall doesn't exist any more. - print(wall) # Kaboom. - """ - self.file = file - self.settings = { - "product": product, - "ifc_class": ifc_class, - "predefined_type": predefined_type, - } - def execute(self): element = self.reassign_class(self.settings["product"], self.settings["ifc_class"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py index cecb5544f3..6a1c48b404 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py @@ -20,212 +20,207 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, product: ifcopenshell.entity_instance): - """Removes a product +def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instance) -> None: + """Removes a product - This is effectively a smart delete function that not only removes a - product, but also all of its relationships. It is always recommended to - use this function to prevent orphaned data in your IFC model. + This is effectively a smart delete function that not only removes a + product, but also all of its relationships. It is always recommended to + use this function to prevent orphaned data in your IFC model. - This is intended to be used for removing: + This is intended to be used for removing: - - IfcAnnotation - - IfcElement - - IfcElementType - - IfcSpatialElement - - IfcSpatialElementType + - IfcAnnotation + - IfcElement + - IfcElementType + - IfcSpatialElement + - IfcSpatialElementType - For example, geometric representations are removed. Placement - coordinates are also removed. Properties are removed. Material, type, - containment, aggregation, and nesting relationships are removed (but - naturally, the materials, types, containers, etc themselves remain). + For example, geometric representations are removed. Placement + coordinates are also removed. Properties are removed. Material, type, + containment, aggregation, and nesting relationships are removed (but + naturally, the materials, types, containers, etc themselves remain). - :param product: The element to remove. - :type product: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The element to remove. + :type product: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # We have a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # We have a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # No we don't. - ifcopenshell.api.run("root.remove_product", model, product=wall) - """ - self.file = file - self.settings = {"product": product} + # No we don't. + ifcopenshell.api.run("root.remove_product", model, product=wall) + """ + settings = {"product": product} - def execute(self) -> None: - representations = [] - if self.settings["product"].is_a("IfcProduct"): - if self.settings["product"].Representation: - representations = self.settings["product"].Representation.Representations or [] - else: - representations = [] + representations = [] + if settings["product"].is_a("IfcProduct"): + if settings["product"].Representation: + representations = settings["product"].Representation.Representations or [] + else: + representations = [] - # remove object placements - object_placement = self.settings["product"].ObjectPlacement - if object_placement: - if self.file.get_total_inverses(object_placement) == 1: - self.settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work - ifcopenshell.util.element.remove_deep2(self.file, object_placement) + # remove object placements + object_placement = settings["product"].ObjectPlacement + if object_placement: + if file.get_total_inverses(object_placement) == 1: + settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work + ifcopenshell.util.element.remove_deep2(file, object_placement) - elif self.settings["product"].is_a("IfcTypeProduct"): - representations = [rm.MappedRepresentation for rm in self.settings["product"].RepresentationMaps or []] + elif settings["product"].is_a("IfcTypeProduct"): + representations = [rm.MappedRepresentation for rm in settings["product"].RepresentationMaps or []] - # remove psets - psets = self.settings["product"].HasPropertySets or [] - for pset in psets: - if self.file.get_total_inverses(pset) != 1: - continue - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["product"], - pset=pset, - ) - - for representation in representations: - ifcopenshell.api.run( - "geometry.unassign_representation", - self.file, - **{"product": self.settings["product"], "representation": representation} - ) - ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation}) - for opening in getattr(self.settings["product"], "HasOpenings", []) or []: - ifcopenshell.api.run("void.remove_opening", self.file, opening=opening.RelatedOpeningElement) - - if self.settings["product"].is_a("IfcGrid"): - for axis in ( - self.settings["product"].UAxes + self.settings["product"].VAxes + (self.settings["product"].WAxes or ()) - ): - ifcopenshell.api.run("grid.remove_grid_axis", self.file, axis=axis) - - def element_exists(element_id): - try: - self.file.by_id(element_id) - return True - except RuntimeError: - return False - - # TODO: remove object placement and other relationships - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["product"])]: - try: - inverse = self.file.by_id(inverse_id) - except: + # remove psets + psets = settings["product"].HasPropertySets or [] + for pset in psets: + if file.get_total_inverses(pset) != 1: continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["product"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssociatesMaterial"): - ifcopenshell.api.run("material.unassign_material", self.file, products=[self.settings["product"]]) - elif inverse.is_a("IfcRelDefinesByType"): - if inverse.RelatingType == self.settings["product"]: - ifcopenshell.api.run("type.unassign_type", self.file, related_objects=inverse.RelatedObjects) - else: - ifcopenshell.api.run("type.unassign_type", self.file, related_objects=[self.settings["product"]]) - elif inverse.is_a("IfcRelSpaceBoundary"): - ifcopenshell.api.run("boundary.remove_boundary", self.file, boundary=inverse) - elif inverse.is_a("IfcRelFillsElement"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelVoidsElement"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelServicesBuildings"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["product"]: - inverse_id = inverse.id() - for subelement in inverse.RelatedObjects: - if subelement.is_a("IfcDistributionPort"): - ifcopenshell.api.run("root.remove_product", self.file, product=subelement) - # IfcRelNests could have been already deleted after removing one of the products - if element_exists(inverse_id): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.RelatedObjects == (self.settings["product"],): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["product"], + pset=pset, + ) + + for representation in representations: + ifcopenshell.api.run( + "geometry.unassign_representation", + file, + **{"product": settings["product"], "representation": representation} + ) + ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation}) + for opening in getattr(settings["product"], "HasOpenings", []) or []: + ifcopenshell.api.run("void.remove_opening", file, opening=opening.RelatedOpeningElement) + + if settings["product"].is_a("IfcGrid"): + for axis in settings["product"].UAxes + settings["product"].VAxes + (settings["product"].WAxes or ()): + ifcopenshell.api.run("grid.remove_grid_axis", file, axis=axis) + + def element_exists(element_id): + try: + file.by_id(element_id) + return True + except RuntimeError: + return False + + # TODO: remove object placement and other relationships + for inverse_id in [i.id() for i in file.get_inverse(settings["product"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["product"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssociatesMaterial"): + ifcopenshell.api.run("material.unassign_material", file, products=[settings["product"]]) + elif inverse.is_a("IfcRelDefinesByType"): + if inverse.RelatingType == settings["product"]: + ifcopenshell.api.run("type.unassign_type", file, related_objects=inverse.RelatedObjects) + else: + ifcopenshell.api.run("type.unassign_type", file, related_objects=[settings["product"]]) + elif inverse.is_a("IfcRelSpaceBoundary"): + ifcopenshell.api.run("boundary.remove_boundary", file, boundary=inverse) + elif inverse.is_a("IfcRelFillsElement"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelVoidsElement"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelServicesBuildings"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["product"]: + inverse_id = inverse.id() + for subelement in inverse.RelatedObjects: + if subelement.is_a("IfcDistributionPort"): + ifcopenshell.api.run("root.remove_product", file, product=subelement) + # IfcRelNests could have been already deleted after removing one of the products + if element_exists(inverse_id): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAggregates"): - if inverse.RelatingObject == self.settings["product"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelContainedInSpatialStructure"): - if inverse.RelatingStructure == self.settings["product"] or len(inverse.RelatedElements) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelConnectsElements"): - if inverse.is_a("IfcRelConnectsWithRealizingElements"): - if self.settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any( - el for el in inverse.RealizingElements if el != self.settings["product"] - ): - continue + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.RelatedObjects == (settings["product"],): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelConnectsPorts"): - if self.settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort): - # if it's not RelatingPort/RelatedPort then it's optional RealizingElement - # so we keep the relationship + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAggregates"): + if inverse.RelatingObject == settings["product"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelContainedInSpatialStructure"): + if inverse.RelatingStructure == settings["product"] or len(inverse.RelatedElements) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelConnectsElements"): + if inverse.is_a("IfcRelConnectsWithRealizingElements"): + if settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any( + el for el in inverse.RealizingElements if el != settings["product"] + ): continue + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelConnectsPorts"): + if settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort): + # if it's not RelatingPort/RelatedPort then it's optional RealizingElement + # so we keep the relationship + continue + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToGroup"): + if len(inverse.RelatedObjects) == 1: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToGroup"): - if len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToProduct"): - if inverse.RelatingProduct == self.settings["product"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelFlowControlElements"): - if inverse.RelatingFlowElement == self.settings["product"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.RelatedControlElements == (self.settings["product"],): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["product"].OwnerHistory - self.file.remove(self.settings["product"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToProduct"): + if inverse.RelatingProduct == settings["product"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelFlowControlElements"): + if inverse.RelatingFlowElement == settings["product"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.RelatedControlElements == (settings["product"],): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["product"].OwnerHistory + file.remove(settings["product"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py index e0caddbe3c..90cb5f4922 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py @@ -15,3 +15,47 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_task import add_task +from .add_task_time import add_task_time +from .add_time_period import add_time_period +from .add_work_calendar import add_work_calendar +from .add_work_plan import add_work_plan +from .add_work_schedule import add_work_schedule +from .add_work_time import add_work_time +from .assign_lag_time import assign_lag_time +from .assign_process import assign_process +from .assign_product import assign_product +from .assign_recurrence_pattern import assign_recurrence_pattern +from .assign_sequence import assign_sequence +from .assign_workplan import assign_workplan +from .calculate_task_duration import calculate_task_duration +from .cascade_schedule import cascade_schedule +from .create_baseline import create_baseline +from .duplicate_task import duplicate_task +from .edit_lag_time import edit_lag_time +from .edit_recurrence_pattern import edit_recurrence_pattern +from .edit_sequence import edit_sequence +from .edit_task import edit_task +from .edit_task_time import edit_task_time +from .edit_work_calendar import edit_work_calendar +from .edit_work_plan import edit_work_plan +from .edit_work_schedule import edit_work_schedule +from .edit_work_time import edit_work_time +from .get_related_products import get_related_products + +try: + from .recalculate_schedule import recalculate_schedule +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: sequence.recalculate_schedule - {e}") +from .remove_task import remove_task +from .remove_time_period import remove_time_period +from .remove_work_calendar import remove_work_calendar +from .remove_work_plan import remove_work_plan +from .remove_work_schedule import remove_work_schedule +from .remove_work_time import remove_work_time +from .unassign_lag_time import unassign_lag_time +from .unassign_process import unassign_process +from .unassign_product import unassign_product +from .unassign_recurrence_pattern import unassign_recurrence_pattern +from .unassign_sequence import unassign_sequence diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py index 60ab48c6df..7d1a91423f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py @@ -20,169 +20,159 @@ import ifcopenshell.api import ifcopenshell -class Usecase: - def __init__( - self, +def add_task( + file, + work_schedule=None, + parent_task=None, + name=None, + description=None, + identification=None, + predefined_type="NOTDEFINED", +) -> None: + """Adds a new task + + Tasks are typically used for two purposes: construction scheduling and + facility management. + + In construction scheduling, a task represents a job to be done in a work + schedule. Tasks are organised in a hierarchical manner known as a work + breakdown structure (WBS) and have lots of sequential relationships + (e.g. this task must finish before the next task can start) and date + information (e.g. durations, start dates). This is often represented as + a gantt chart and used to analyse critical paths to try and reduce + project time to stay on-time and within budget. + + In facility management, a task represents a maintenance task to maintain + a piece of equipment. Tasks are broken down into a punch list, or simply + a bulleted or ordered sequence of tasks to be performed (e.g. turn off + equipment, check power connection, etc) in order to maintain the + equipment. Tasks will also typically have recurring scheduled dates in + line with the maintenance schedule. These maintenance tasks and + procedures are typically published as part of an operations and + maintenance manual. + + All tasks must be grouped in a work schedule, either directly as a root + or top-level task, or indirectly as a child or subtask of a parent task. + In construction scheduling, tasks may be nested many times to create the + work breakdown structure, and the "leaf" tasks (i.e. tasks with no more + subtasks) are considered to be the activities with dates, whereas all + parent tasks are part of the breakdown structure used for categorisation + purposes. In facility management, top-level tasks represent the overall + maintenance job to be performed, and child tasks represent an ordered + list of things to do for that maintenance. These form a 2-level + hierarchy. No further child tasks are recommended. + + :param work_schedule: The work schedule to group the task in, if the + task is to be a top-level or root task. This is mutually exclusive + with the parent_task parameter. + :type work_schedule: ifcopenshell.entity_instance + :param parent_task: The parent task, if the task is to be a subtask or + child task. This is mutually exclusive with the work_schedule + parameter. + :type parent_task: ifcopenshell.entity_instance + :param name: The name of the task. + :type name: str,optional + :param description: The description of the task. + :type description: str,optional + :param identification: The identification code of the task. + :type identification: str,optional + :param predefined_type: The predefined type of the task. Common ones + include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the + IFC documentation for IfcTaskTypeEnum for more information. + :type predefined_type: str + :return: The newly created IfcTask + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + + # Let's start creating our work breakdown structure. + ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Early Works", identification="C1") + ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Substructure", identification="C2") + superstructure = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Superstructure", identification="C3") + + # Notice how the leaf task is the actual activity + ifcopenshell.api.run("sequence.add_task", model, + parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") + + # Let's imagine we are digitising an operations and maintenance + # manual for the mechanical discipline. + maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance") + + # Imagine we have to clean the condenser coils for a chiller every + # month. Like the schedule above, to keep things simple we won't + # show scheduling times and calendars. This root task represents the + # overall maintenance task. + cleaning = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=maintenance, name="Condenser coil cleaning") + + # These subtasks represent the punch list of maintenance tasks. + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1", + description="Prior to work, wear safety shoes, gloves, and goggles.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2", + description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Switch OFF the chiller unit.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Open the isolator switch.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Setup the water pressure by tapping to a water supply and connecting to a ...") + """ + settings = { + "work_schedule": work_schedule, + "parent_task": parent_task, + "name": name, + "description": description, + "identification": identification, + "predefined_type": predefined_type, + } + + task = ifcopenshell.api.run( + "root.create_entity", file, - work_schedule=None, - parent_task=None, - name=None, - description=None, - identification=None, - predefined_type="NOTDEFINED", - ): - """Adds a new task - - Tasks are typically used for two purposes: construction scheduling and - facility management. - - In construction scheduling, a task represents a job to be done in a work - schedule. Tasks are organised in a hierarchical manner known as a work - breakdown structure (WBS) and have lots of sequential relationships - (e.g. this task must finish before the next task can start) and date - information (e.g. durations, start dates). This is often represented as - a gantt chart and used to analyse critical paths to try and reduce - project time to stay on-time and within budget. - - In facility management, a task represents a maintenance task to maintain - a piece of equipment. Tasks are broken down into a punch list, or simply - a bulleted or ordered sequence of tasks to be performed (e.g. turn off - equipment, check power connection, etc) in order to maintain the - equipment. Tasks will also typically have recurring scheduled dates in - line with the maintenance schedule. These maintenance tasks and - procedures are typically published as part of an operations and - maintenance manual. - - All tasks must be grouped in a work schedule, either directly as a root - or top-level task, or indirectly as a child or subtask of a parent task. - In construction scheduling, tasks may be nested many times to create the - work breakdown structure, and the "leaf" tasks (i.e. tasks with no more - subtasks) are considered to be the activities with dates, whereas all - parent tasks are part of the breakdown structure used for categorisation - purposes. In facility management, top-level tasks represent the overall - maintenance job to be performed, and child tasks represent an ordered - list of things to do for that maintenance. These form a 2-level - hierarchy. No further child tasks are recommended. - - :param work_schedule: The work schedule to group the task in, if the - task is to be a top-level or root task. This is mutually exclusive - with the parent_task parameter. - :type work_schedule: ifcopenshell.entity_instance - :param parent_task: The parent task, if the task is to be a subtask or - child task. This is mutually exclusive with the work_schedule - parameter. - :type parent_task: ifcopenshell.entity_instance - :param name: The name of the task. - :type name: str,optional - :param description: The description of the task. - :type description: str,optional - :param identification: The identification code of the task. - :type identification: str,optional - :param predefined_type: The predefined type of the task. Common ones - include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the - IFC documentation for IfcTaskTypeEnum for more information. - :type predefined_type: str - :return: The newly created IfcTask - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - - # Let's start creating our work breakdown structure. - ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Early Works", identification="C1") - ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Substructure", identification="C2") - superstructure = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Superstructure", identification="C3") - - # Notice how the leaf task is the actual activity - ifcopenshell.api.run("sequence.add_task", model, - parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") - - # Let's imagine we are digitising an operations and maintenance - # manual for the mechanical discipline. - maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance") - - # Imagine we have to clean the condenser coils for a chiller every - # month. Like the schedule above, to keep things simple we won't - # show scheduling times and calendars. This root task represents the - # overall maintenance task. - cleaning = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=maintenance, name="Condenser coil cleaning") - - # These subtasks represent the punch list of maintenance tasks. - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1", - description="Prior to work, wear safety shoes, gloves, and goggles.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2", - description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Switch OFF the chiller unit.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Open the isolator switch.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Setup the water pressure by tapping to a water supply and connecting to a ...") - """ - self.file = file - self.settings = { - "work_schedule": work_schedule, - "parent_task": parent_task, - "name": name, - "description": description, - "identification": identification, - "predefined_type": predefined_type, - } - - def execute(self): - task = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcTask", - name=self.settings["name"], - predefined_type=self.settings["predefined_type"], + ifc_class="IfcTask", + name=settings["name"], + predefined_type=settings["predefined_type"], + ) + if settings["description"]: + task.Description = settings["description"] + if settings["identification"]: + task.Identification = settings["identification"] + task.IsMilestone = False + if settings["work_schedule"]: + file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [task], + "RelatingControl": settings["work_schedule"], + } ) - if self.settings["description"]: - task.Description = self.settings["description"] - if self.settings["identification"]: - task.Identification = self.settings["identification"] - task.IsMilestone = False - if self.settings["work_schedule"]: - self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [task], - "RelatingControl": self.settings["work_schedule"], - } - ) - elif self.settings["parent_task"]: - rel = ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[task], - relating_object=self.settings["parent_task"], - ) - if self.settings["parent_task"].Identification: - task.Identification = ( - self.settings["parent_task"].Identification - + "." - + str(len(rel.RelatedObjects)) - ) - return task + elif settings["parent_task"]: + rel = ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[task], + relating_object=settings["parent_task"], + ) + if settings["parent_task"].Identification: + task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects)) + return task diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py index 7c4381c361..bbd51c2e68 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py @@ -17,55 +17,52 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, task=None, is_recurring=False): - """Adds a task time to a task +def add_task_time(file, task=None, is_recurring=False) -> None: + """Adds a task time to a task - Some tasks, such as activities within a work breakdown structure or - overall maintenance tasks will have time related information. This - includes start dates, durations, end dates, and possible recurring times - (especially for maintenance tasks). + Some tasks, such as activities within a work breakdown structure or + overall maintenance tasks will have time related information. This + includes start dates, durations, end dates, and possible recurring times + (especially for maintenance tasks). - :param task: The task to add time data to. - :type task: ifcopenshell.entity_instance - :param is_recurring: Whether or not the time should recur. - :type is_recurring: bool - :return: The newly created IfcTaskTime. - :rtype: ifcopenshell.entity_instance + :param task: The task to add time data to. + :type task: ifcopenshell.entity_instance + :param is_recurring: Whether or not the time should recur. + :type is_recurring: bool + :return: The newly created IfcTaskTime. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Create a portion of a work breakdown structure. - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - superstructure = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Superstructure", identification="C3") - task = ifcopenshell.api.run("sequence.add_task", model, - parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") + # Create a portion of a work breakdown structure. + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + superstructure = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Superstructure", identification="C3") + task = ifcopenshell.api.run("sequence.add_task", model, + parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") - # Add time data. Note that time data is blank by default. - time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) + # Add time data. Note that time data is blank by default. + time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) - # Let's say our task starts on the first of January when everybody - # is still drunk from the new years celebration, and lasts for 2 - # days. Note we don't need to specify the end date, as that is - # derived from the start plus the duration. In this simple example, - # no calendar has been specified, so we are working 24/7. Yikes! - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - """ - self.file = file - self.settings = {"task": task, "is_recurring": is_recurring} + # Let's say our task starts on the first of January when everybody + # is still drunk from the new years celebration, and lasts for 2 + # days. Note we don't need to specify the end date, as that is + # derived from the start plus the duration. In this simple example, + # no calendar has been specified, so we are working 24/7. Yikes! + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + """ + settings = {"task": task, "is_recurring": is_recurring} - def execute(self): - if self.settings["is_recurring"]: - task_time = self.file.create_entity("IfcTaskTimeRecurring") - else: - task_time = self.file.create_entity("IfcTaskTime") - self.settings["task"].TaskTime = task_time - return task_time + if settings["is_recurring"]: + task_time = file.create_entity("IfcTaskTimeRecurring") + else: + task_time = file.create_entity("IfcTaskTime") + settings["task"].TaskTime = task_time + return task_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py index 35a022a9ea..479524a4b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py @@ -23,79 +23,72 @@ from datetime import datetime from datetime import timedelta -class Usecase: - def __init__(self, file, recurrence_pattern=None, start_time=None, end_time=None): - """Adds a time period to a recurrence pattern +def add_time_period(file, recurrence_pattern=None, start_time=None, end_time=None) -> None: + """Adds a time period to a recurrence pattern - A recurring time may be an all-day event, or only during certain time - periods of the day. For example, you might say that every 1st of January - recurring is a public holiday, which is an all-day event. Alternatively, - you might say that you work every (i.e. recurringly) Monday to Friday, - from 9am to 5pm. The 9am to 5pm is the time period. + A recurring time may be an all-day event, or only during certain time + periods of the day. For example, you might say that every 1st of January + recurring is a public holiday, which is an all-day event. Alternatively, + you might say that you work every (i.e. recurringly) Monday to Friday, + from 9am to 5pm. The 9am to 5pm is the time period. - There may also be multiple recurrence patterns, such as from 9am to - 12pm, and then another from 1pm to 5pm (to indicate an hour break for - lunch). + There may also be multiple recurrence patterns, such as from 9am to + 12pm, and then another from 1pm to 5pm (to indicate an hour break for + lunch). - :param recurrence_pattern: The IfcRecurrencePattern to add the time - period to. See ifcopenshell.api.sequence.assign_recurrence_pattern. - :type recurrence_pattern: ifcopenshell.entity_instance - :param start_time: The start time of the time period, in a format - compatible with IfcTime, such as an ISO format time string or a - datetime.time object. - :type start_time: str,datetime.time - :param end_time: The end time of the time period, in a format - compatible with IfcTime, such as an ISO format time string or a - datetime.time object. - :type end_time: str,datetime.time - :return: The newly created IfcTimePeriod - :rtype: ifcopenshell.entity_instance + :param recurrence_pattern: The IfcRecurrencePattern to add the time + period to. See ifcopenshell.api.sequence.assign_recurrence_pattern. + :type recurrence_pattern: ifcopenshell.entity_instance + :param start_time: The start time of the time period, in a format + compatible with IfcTime, such as an ISO format time string or a + datetime.time object. + :type start_time: str,datetime.time + :param end_time: The end time of the time period, in a format + compatible with IfcTime, such as an ISO format time string or a + datetime.time object. + :type end_time: str,datetime.time + :return: The newly created IfcTimePeriod + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # The morning work session, lunch, then the afternoon work session. - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="12:00") - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="13:00", end_time="17:00") - """ - self.file = file - self.settings = { - "recurrence_pattern": recurrence_pattern, - "start_time": start_time, - "end_time": end_time, - } + # The morning work session, lunch, then the afternoon work session. + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="12:00") + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="13:00", end_time="17:00") + """ + settings = { + "recurrence_pattern": recurrence_pattern, + "start_time": start_time, + "end_time": end_time, + } - def execute(self): - time_period = self.file.create_entity("IfcTimePeriod") - time_period.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcTime" - ) - time_period.EndTime = ifcopenshell.util.date.datetime2ifc( - self.settings["end_time"], "IfcTime" - ) - time_periods = list(self.settings["recurrence_pattern"].TimePeriods or []) - time_periods.append(time_period) - self.settings["recurrence_pattern"].TimePeriods = time_periods + time_period = file.create_entity("IfcTimePeriod") + time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime") + time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime") + time_periods = list(settings["recurrence_pattern"].TimePeriods or []) + time_periods.append(time_period) + settings["recurrence_pattern"].TimePeriods = time_periods - ifcopenshell.util.sequence.is_working_day.cache_clear() - ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() + ifcopenshell.util.sequence.is_working_day.cache_clear() + ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() - return time_period + return time_period diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index 373d628be6..250a1b2fe0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -19,80 +19,77 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, name="Unnamed", predefined_type="NOTDEFINED"): - """Add a work calendar +def add_work_calendar(file, name="Unnamed", predefined_type="NOTDEFINED") -> None: + """Add a work calendar - A work calendar defines when work is allowed to occur and when the - holidays are. This is a fundamental concept in construction planning. - Every task in a work schedule will have an associated calendar. Some - task and resources work 24/7, whereas others work Monday to Friday, or - 5.5 day weeks, etc. This is important, as tasks durations may only occur - during working times in a work calendar. + A work calendar defines when work is allowed to occur and when the + holidays are. This is a fundamental concept in construction planning. + Every task in a work schedule will have an associated calendar. Some + task and resources work 24/7, whereas others work Monday to Friday, or + 5.5 day weeks, etc. This is important, as tasks durations may only occur + during working times in a work calendar. - Work calendars can also be used to associate with events, such as - indicating that during certain days and times of the year, motion - sensors should turn on the lights, and other smart building controls. + Work calendars can also be used to associate with events, such as + indicating that during certain days and times of the year, motion + sensors should turn on the lights, and other smart building controls. - :param name: The name of the calendar. Typically something like - "5 Day Working Week" or "24/7". - :type name: str, optional - :param predefined_type: The type of calendar, typically used to more - specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or - THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage. - :return: The newly created IfcWorkCalendar - :rtype: ifcopenshell.entity_instance + :param name: The name of the calendar. Typically something like + "5 Day Working Week" or "24/7". + :type name: str, optional + :param predefined_type: The type of calendar, typically used to more + specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or + THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage. + :return: The newly created IfcWorkCalendar + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the construction tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Add a root task to represent the construction tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="17:00") + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="17:00") - # We associate the calendar with the construction root task. All - # subtasks underneath the construction work task will also inherit - # this calendar by default (though you can override them). - ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task) - """ - self.file = file - self.settings = {"name": name, "predefined_type": predefined_type} + # We associate the calendar with the construction root task. All + # subtasks underneath the construction work task will also inherit + # this calendar by default (though you can override them). + ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task) + """ + settings = {"name": name, "predefined_type": predefined_type} - def execute(self): - work_calendar = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkCalendar", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_calendar], - relating_context=context, - ) - return work_calendar + work_calendar = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcWorkCalendar", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_calendar], + relating_context=context, + ) + return work_calendar diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py index f6fba71315..858d944d66 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py @@ -21,70 +21,63 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__(self, file, name=None, predefined_type="NOTDEFINED", start_time=None): - """Add a new work plan +def add_work_plan(file, name=None, predefined_type="NOTDEFINED", start_time=None) -> None: + """Add a new work plan - A work plan is a group of work schedules. Since work schedules may have - different purposes, such as for maintenance or construction scheduling, - baseline comparison, or phasing, work plans can be used to group related - work schedules. At a minimum, it is recommended to use work plans to - indicate whether the work schedules are for facility management or for - construction scheduling. + A work plan is a group of work schedules. Since work schedules may have + different purposes, such as for maintenance or construction scheduling, + baseline comparison, or phasing, work plans can be used to group related + work schedules. At a minimum, it is recommended to use work plans to + indicate whether the work schedules are for facility management or for + construction scheduling. - :param name: The name of the work plan. Recommended to be "Maintenance" - or "Construction" for the two main purposes. - :type name: str, optional - :param predefined_type: The type of work plan, used for baselining. - Leave as "NOTDEFINED" if unsure. - :type predefined_type: str - :param start_time: The earliest start time when the schedules grouped - within the work plan are relevant. - :type start_time: str,datetime.time - :return: The newly created IfcWorkPlan - :rtype: ifcopenshell.entity_instance + :param name: The name of the work plan. Recommended to be "Maintenance" + or "Construction" for the two main purposes. + :type name: str, optional + :param predefined_type: The type of work plan, used for baselining. + Leave as "NOTDEFINED" if unsure. + :type predefined_type: str + :param start_time: The earliest start time when the schedules grouped + within the work plan are relevant. + :type start_time: str,datetime.time + :return: The newly created IfcWorkPlan + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # This is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) - """ - self.file = file - self.settings = { - "name": name, - "predefined_type": predefined_type, - "start_time": start_time or datetime.now(), - } + # This is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) + """ + settings = { + "name": name, + "predefined_type": predefined_type, + "start_time": start_time or datetime.now(), + } - def execute(self): - work_plan = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkPlan", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc( - datetime.now(), "IfcDateTime" - ) - user = ifcopenshell.api.owner.settings.get_user(self.file) - if user: - work_plan.Creators = [user.ThePerson] - work_plan.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcDateTime" - ) + work_plan = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcWorkPlan", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + user = ifcopenshell.api.owner.settings.get_user(file) + if user: + work_plan.Creators = [user.ThePerson] + work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime") - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_plan], - relating_context=context, - ) - return work_plan + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_plan], + relating_context=context, + ) + return work_plan diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index 47e96a8fa3..21f508999c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -21,104 +21,96 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__( - self, +def add_work_schedule( + file, + name="Unnamed", + predefined_type="NOTDEFINED", + object_type=None, + start_time=None, + work_plan=None, +) -> None: + """Add a new work schedule + + A work schedule is a group of tasks, where the tasks are typically + either for maintenance or for construction scheduling. + + :param name: The name of the work schedule. + :type name: str + :param predefined_type: The type of schedule, chosen from ACTUAL, + BASELINE, and PLANNED. Typically you would start with PLANNED, then + convert to a BASELINE when changes are made with separate schedules, + then have a parallel ACTUAL schedule. + :type predefined_type: str + :param start_time: The earlier start time when the schedule is relevant. + May be represented with an ISO standard string. + :type start_time: str,datetime.time,optional + :param work_plan: The IfcWorkPlan the schedule will be part of. If not + provided, the schedule will not be grouped in a work plan and would + exist as a top level schedule in the project. This is not + recommended. + :type work_plan: ifcopenshell.entity_instance,optional + :return: The newly created IfcWorkSchedule + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) + + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + """ + settings = { + "name": name, + "predefined_type": predefined_type, + "object_type": object_type, + "start_time": start_time or datetime.now(), + "work_plan": work_plan, + } + + work_schedule = ifcopenshell.api.run( + "root.create_entity", file, - name="Unnamed", - predefined_type="NOTDEFINED", - object_type=None, - start_time=None, - work_plan=None, - ): - """Add a new work schedule - - A work schedule is a group of tasks, where the tasks are typically - either for maintenance or for construction scheduling. - - :param name: The name of the work schedule. - :type name: str - :param predefined_type: The type of schedule, chosen from ACTUAL, - BASELINE, and PLANNED. Typically you would start with PLANNED, then - convert to a BASELINE when changes are made with separate schedules, - then have a parallel ACTUAL schedule. - :type predefined_type: str - :param start_time: The earlier start time when the schedule is relevant. - May be represented with an ISO standard string. - :type start_time: str,datetime.time,optional - :param work_plan: The IfcWorkPlan the schedule will be part of. If not - provided, the schedule will not be grouped in a work plan and would - exist as a top level schedule in the project. This is not - recommended. - :type work_plan: ifcopenshell.entity_instance,optional - :return: The newly created IfcWorkSchedule - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) - - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - """ - self.file = file - self.settings = { - "name": name, - "predefined_type": predefined_type, - "object_type": object_type, - "start_time": start_time or datetime.now(), - "work_plan": work_plan, - } - - def execute(self): - work_schedule = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkSchedule", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], + ifc_class="IfcWorkSchedule", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + user = ifcopenshell.api.owner.settings.get_user(file) + if user: + work_schedule.Creators = [user.ThePerson] + work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime") + if settings["object_type"]: + work_schedule.ObjectType = settings["object_type"] + if settings["work_plan"]: + ifcopenshell.api.run( + "aggregate.assign_object", + file, + **{ + "products": [work_schedule], + "relating_object": settings["work_plan"], + } ) - work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc( - datetime.now(), "IfcDateTime" + else: + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_schedule], + relating_context=context, ) - user = ifcopenshell.api.owner.settings.get_user(self.file) - if user: - work_schedule.Creators = [user.ThePerson] - work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcDateTime" - ) - if self.settings["object_type"]: - work_schedule.ObjectType = self.settings["object_type"] - if self.settings["work_plan"]: - ifcopenshell.api.run( - "aggregate.assign_object", - self.file, - **{ - "products": [work_schedule], - "relating_object": self.settings["work_plan"], - } - ) - else: - # TODO: this is an ambiguity by buildingSMART - # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_schedule], - relating_context=context, - ) - return work_schedule + return work_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py index 0d75914949..86d4666ad9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py @@ -17,69 +17,66 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_calendar=None, time_type="WorkingTimes"): - """Add either working times or holiday times to a calendar +def add_work_time(file, work_calendar=None, time_type="WorkingTimes") -> None: + """Add either working times or holiday times to a calendar - A calendar defines when work occurs by defining working times and - holiday times. First, the working times are defined, then the holidays - may override the working times. For this reason, holidays are also known - as exception times. For example, you might define the working times as - every Monday to Friday, then define a few holidays in the year, such as - the 1st of January. If the 1st of January is on a weekday, it will - override the work time. + A calendar defines when work occurs by defining working times and + holiday times. First, the working times are defined, then the holidays + may override the working times. For this reason, holidays are also known + as exception times. For example, you might define the working times as + every Monday to Friday, then define a few holidays in the year, such as + the 1st of January. If the 1st of January is on a weekday, it will + override the work time. - :param work_calendar: The IfcWorkCalendar to add the work or holiday - time definition to. - :type work_calendar: ifcopenshell.entity_instance - :param time_type: Either WorkingTimes or ExceptionTimes, depending on - what you want to define. - :type time_type: str - :return: The newly created IfcWorkTime - :rtype: ifcopenshell.entity_instance + :param work_calendar: The IfcWorkCalendar to add the work or holiday + time definition to. + :type work_calendar: ifcopenshell.entity_instance + :param time_type: Either WorkingTimes or ExceptionTimes, depending on + what you want to define. + :type time_type: str + :return: The newly created IfcWorkTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # Let's set some holidays - holidays = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="ExceptionTimes") + # Let's set some holidays + holidays = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="ExceptionTimes") - # We create a yearly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH") + # We create a yearly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH") - # The holiday is every 1st of January - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]}) - """ - self.file = file - self.settings = {"work_calendar": work_calendar, "time_type": time_type} + # The holiday is every 1st of January + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]}) + """ + settings = {"work_calendar": work_calendar, "time_type": time_type} - def execute(self): - work_time = self.file.create_entity("IfcWorkTime") - if self.settings["time_type"] == "WorkingTimes": - working_times = list(self.settings["work_calendar"].WorkingTimes or []) - working_times.append(work_time) - self.settings["work_calendar"].WorkingTimes = working_times - elif self.settings["time_type"] == "ExceptionTimes": - exception_times = list(self.settings["work_calendar"].ExceptionTimes or []) - exception_times.append(work_time) - self.settings["work_calendar"].ExceptionTimes = exception_times - return work_time + work_time = file.create_entity("IfcWorkTime") + if settings["time_type"] == "WorkingTimes": + working_times = list(settings["work_calendar"].WorkingTimes or []) + working_times.append(work_time) + settings["work_calendar"].WorkingTimes = working_times + elif settings["time_type"] == "ExceptionTimes": + exception_times = list(settings["work_calendar"].ExceptionTimes or []) + exception_times.append(work_time) + settings["work_calendar"].ExceptionTimes = exception_times + return work_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py index 87f2882055..0ba89d0346 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py @@ -19,88 +19,78 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, rel_sequence=None, lag_value=None, duration_type="WORKTIME"): - """Assign a lag time to a sequence relationship between tasks +def assign_lag_time(file, rel_sequence=None, lag_value=None, duration_type="WORKTIME") -> None: + """Assign a lag time to a sequence relationship between tasks - A task sequence (e.g. finish to start) may optionally have a lag time - defined. This is a fundamental concept in construction scheduling. The - lag is defined as a duration, and the duration is typically either - calendar based (i.e. follows the working times and holidays of the - calendar) or elapsed time based (i.e. 24/7). + A task sequence (e.g. finish to start) may optionally have a lag time + defined. This is a fundamental concept in construction scheduling. The + lag is defined as a duration, and the duration is typically either + calendar based (i.e. follows the working times and holidays of the + calendar) or elapsed time based (i.e. 24/7). - A sequence may only have a single lag time defined. Negative lag times - are allowed. + A sequence may only have a single lag time defined. Negative lag times + are allowed. - :param rel_sequence: The IfcRelSequence to assign the lag time to. - :type rel_sequence: ifcopenshell.entity_instance - :param lag_value: An ISO standardised duration string. - :type lag_value: str - :param duration_type: Choose from WORKTIME for the associated - calendar-based lag times (this is the most common scenario and is - recommended as a default), or ELAPSEDTIME to not follow the - calendar. You may also choose NOTDEFINED but the behaviour of this - is unclear. - :type duration_type: str - :return: The newly created IfcLagTime - :rtype: ifcopenshell.entity_instance + :param rel_sequence: The IfcRelSequence to assign the lag time to. + :type rel_sequence: ifcopenshell.entity_instance + :param lag_value: An ISO standardised duration string. + :type lag_value: str + :param duration_type: Choose from WORKTIME for the associated + calendar-based lag times (this is the most common scenario and is + recommended as a default), or ELAPSEDTIME to not follow the + calendar. You may also choose NOTDEFINED but the behaviour of this + is unclear. + :type duration_type: str + :return: The newly created IfcLagTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's say the formwork must finish before the reinforcement - # can start. This is a typical finish to start relationship (FS). - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) + # Now let's say the formwork must finish before the reinforcement + # can start. This is a typical finish to start relationship (FS). + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) - # Now typically there would be no lag time between formwork and - # reinforcement, but let's pretend that we had to allow 1 day gap - # for whatever reason. - ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") - """ - self.file = file - self.settings = { - "rel_sequence": rel_sequence, - "lag_value": lag_value, - "duration_type": duration_type, - } + # Now typically there would be no lag time between formwork and + # reinforcement, but let's pretend that we had to allow 1 day gap + # for whatever reason. + ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") + """ + settings = { + "rel_sequence": rel_sequence, + "lag_value": lag_value, + "duration_type": duration_type, + } - def execute(self): - lag_value = self.file.createIfcDuration( - ifcopenshell.util.date.datetime2ifc(self.settings["lag_value"], "IfcDuration") - ) - lag_time = self.file.create_entity( - "IfcLagTime", DurationType=self.settings["duration_type"], LagValue=lag_value - ) - if self.settings["rel_sequence"].is_a("IfcRelSequence"): - if ( - self.settings["rel_sequence"].TimeLag - and len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1 - ): - self.file.remove(self.settings["rel_sequence"].TimeLag) - self.settings["rel_sequence"].TimeLag = lag_time + lag_value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(settings["lag_value"], "IfcDuration")) + lag_time = file.create_entity("IfcLagTime", DurationType=settings["duration_type"], LagValue=lag_value) + if settings["rel_sequence"].is_a("IfcRelSequence"): + if settings["rel_sequence"].TimeLag and len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1: + file.remove(settings["rel_sequence"].TimeLag) + settings["rel_sequence"].TimeLag = lag_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py index 5aa2210d42..12f8cfe78c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py @@ -20,109 +20,99 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_process=None, related_object=None): - """Assigns an object to be related to a process, typically a construction task +def assign_process(file, relating_process=None, related_object=None) -> None: + """Assigns an object to be related to a process, typically a construction task - Processes work using the ICOM (Input, Controls, Outputs, Mechanisms) - paradigm in IFC. This process model is commonly used in modeling - manufacturing functions. + Processes work using the ICOM (Input, Controls, Outputs, Mechanisms) + paradigm in IFC. This process model is commonly used in modeling + manufacturing functions. - For example, processes (such as tasks) consume Inputs and transform them - into Outputs. The process may only occur within the limits of Controls - (e.g. cost items) and may require Mechanisms (ISO9000 calls them - Mechanisms, whereas IFC calls them resources, such as raw materials, - labour, or equipment). + For example, processes (such as tasks) consume Inputs and transform them + into Outputs. The process may only occur within the limits of Controls + (e.g. cost items) and may require Mechanisms (ISO9000 calls them + Mechanisms, whereas IFC calls them resources, such as raw materials, + labour, or equipment). - +----------+ - | Controls | - +----------+ - | - V - +--------+ +---------+ +---------+ - | Inputs | --> | Process | --> | Outputs | - +--------+ +---------+ +---------+ - ^ - | - +-----------+ - | Resources | - +-----------+ + +----------+ + | Controls | + +----------+ + | + V + +--------+ +---------+ +---------+ + | Inputs | --> | Process | --> | Outputs | + +--------+ +---------+ +---------+ + ^ + | + +-----------+ + | Resources | + +-----------+ - There are three main scenarios where an object may be related to a - task: defining inputs, controls, and resources of a process. + There are three main scenarios where an object may be related to a + task: defining inputs, controls, and resources of a process. - For inputs, a product (i.e. wall) may be defined as an input to a task, - such as when the task is to demolish the wall (i.e. the wall is an - input, and there is no output). + For inputs, a product (i.e. wall) may be defined as an input to a task, + such as when the task is to demolish the wall (i.e. the wall is an + input, and there is no output). - For controls, a cost item may be defined as a control to a task. + For controls, a cost item may be defined as a control to a task. - For resources, any construction resource may be assigned to a task. + For resources, any construction resource may be assigned to a task. - :param relating_process: The IfcProcess (typically IfcTask) that the - input, control, or resource is related to. - :type relating_process: ifcopenshell.entity_instance - :param related_object: The IfcProduct (for input), IfcCostItem (for - control) or IfcConstructionResource (for resource). - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToProcess relationship - :rtype: ifcopenshell.entity_instance + :param relating_process: The IfcProcess (typically IfcTask) that the + input, control, or resource is related to. + :type relating_process: ifcopenshell.entity_instance + :param related_object: The IfcProduct (for input), IfcCostItem (for + control) or IfcConstructionResource (for resource). + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToProcess relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's demolish that wall! - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_object": related_object, - } + # Let's demolish that wall! + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) + """ + settings = { + "relating_process": relating_process, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToProcess") - and assignment.RelatingProcess == self.settings["relating_process"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]: + return - operates_on = None - if self.settings["relating_process"].OperatesOn: - operates_on = self.settings["relating_process"].OperatesOn[0] + operates_on = None + if settings["relating_process"].OperatesOn: + operates_on = settings["relating_process"].OperatesOn[0] - if operates_on: - related_objects = list(operates_on.RelatedObjects) - related_objects.append(self.settings["related_object"]) - operates_on.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": operates_on} - ) - else: - operates_on = self.file.create_entity( - "IfcRelAssignsToProcess", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProcess": self.settings["relating_process"], - } - ) - return operates_on + if operates_on: + related_objects = list(operates_on.RelatedObjects) + related_objects.append(settings["related_object"]) + operates_on.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": operates_on}) + else: + operates_on = file.create_entity( + "IfcRelAssignsToProcess", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProcess": settings["relating_process"], + } + ) + return operates_on diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py index 3431a71f19..bd9b90d2da 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py @@ -20,85 +20,75 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Assigns a product to be produced as a result of a process +def assign_product(file, relating_product=None, related_object=None) -> None: + """Assigns a product to be produced as a result of a process - A construction task may result in products (e.g. a wall) being - constructed. These task "Outputs" are defined in IFC through product - relationships. + A construction task may result in products (e.g. a wall) being + constructed. These task "Outputs" are defined in IFC through product + relationships. - Not all tasks have Outputs. For example, maintenance tasks will - typically not have any outputs. + Not all tasks have Outputs. For example, maintenance tasks will + typically not have any outputs. - See ifcopenshell.api.sequence.assign_process for Inputs and other types - of process relationships that can be described in manufacturing - process modeling. + See ifcopenshell.api.sequence.assign_process for Inputs and other types + of process relationships that can be described in manufacturing + process modeling. - :param relating_product: The IfcProduct that was constructed as a result - of the task. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcProcess (typically IfcTask) of the - construction task. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct that was constructed as a result + of the task. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcProcess (typically IfcTask) of the + construction task. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToProduct") - and assignment.RelatingProduct == self.settings["relating_product"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]: + return - referenced_by = None - if self.settings["relating_product"].ReferencedBy: - referenced_by = self.settings["relating_product"].ReferencedBy[0] + referenced_by = None + if settings["relating_product"].ReferencedBy: + referenced_by = settings["relating_product"].ReferencedBy[0] - if referenced_by: - related_objects = list(referenced_by.RelatedObjects) - related_objects.append(self.settings["related_object"]) - referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": referenced_by} - ) - else: - referenced_by = self.file.create_entity( - "IfcRelAssignsToProduct", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProduct": self.settings["relating_product"], - } - ) - return referenced_by + if referenced_by: + related_objects = list(referenced_by.RelatedObjects) + related_objects.append(settings["related_object"]) + referenced_by.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by}) + else: + referenced_by = file.create_entity( + "IfcRelAssignsToProduct", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProduct": settings["relating_product"], + } + ) + return referenced_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py index a3243f1057..177d90fb8d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py @@ -17,112 +17,101 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None, recurrence_type="WEEKLY"): - """Define a time to recur at a particular interval +def assign_recurrence_pattern(file, parent=None, recurrence_type="WEEKLY") -> None: + """Define a time to recur at a particular interval - There are two scenarios where you might want to define a recurring time - pattern. + There are two scenarios where you might want to define a recurring time + pattern. - You might want a task to be scheduled at a recurring interval, - this is common for maintenance tasks which need to be performed monthly, - every 6 months, every year, etc. + You might want a task to be scheduled at a recurring interval, + this is common for maintenance tasks which need to be performed monthly, + every 6 months, every year, etc. - Alternatively, you might be defining a work calendar, which defines - working days or holidays. The working days might be every week from - monday to friday ("every" week means it recurs every week), or the - holidays might be the same every year. + Alternatively, you might be defining a work calendar, which defines + working days or holidays. The working days might be every week from + monday to friday ("every" week means it recurs every week), or the + holidays might be the same every year. - The types of recurrence are: + The types of recurrence are: - - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences. - e.g. Every day, every 2 days, every day up to 5 times, etc - - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X - (Occurrences) occurrences. e.g. Every Monday, every weekday, every - other saturday, etc - - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth - (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of - the Month. - - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent) - of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g. - Every second Tuesday of the Month. - - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND - (MonthComponent) month of every Yth (Interval) Year up to Z - (Occurrences) occurrences. e.g. every 25th of December. - - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of - every JFMAMJJASOND (MonthComponent) month of every Yth (Interval) - Year up to Z (Occurrences) occurrences. e.g. every third Wednesday - of January. + - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences. + e.g. Every day, every 2 days, every day up to 5 times, etc + - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X + (Occurrences) occurrences. e.g. Every Monday, every weekday, every + other saturday, etc + - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth + (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of + the Month. + - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent) + of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g. + Every second Tuesday of the Month. + - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND + (MonthComponent) month of every Yth (Interval) Year up to Z + (Occurrences) occurrences. e.g. every 25th of December. + - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of + every JFMAMJJASOND (MonthComponent) month of every Yth (Interval) + Year up to Z (Occurrences) occurrences. e.g. every third Wednesday + of January. - These recurrence patterns are fairly standard in all calendar and - scheduling applications. + These recurrence patterns are fairly standard in all calendar and + scheduling applications. - :param parent: Either an IfcTaskTimeRecurring if you are defining a - recurring schedule for a task, or IfcWorkTime if you are defining a - recurring pattern for a workdays or holidays in a calendar. - :type parent: ifcopenshell.entity_instance - :param recurrence_type: One of the types of recurrences. - :type recurrence_type: str - :return: The newly created IfcRecurrencePattern - :rtype: ifcopenshell.entity_instance + :param parent: Either an IfcTaskTimeRecurring if you are defining a + recurring schedule for a task, or IfcWorkTime if you are defining a + recurring pattern for a workdays or holidays in a calendar. + :type parent: ifcopenshell.entity_instance + :param recurrence_type: One of the types of recurrences. + :type recurrence_type: str + :return: The newly created IfcRecurrencePattern + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # Let's imagine we are creating a maintenance schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance") + # Let's imagine we are creating a maintenance schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance") - # Now let's imagine we have a task to maintain the chillers - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Chiller maintenance") + # Now let's imagine we have a task to maintain the chillers + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Chiller maintenance") - # Because it is a maintenance task, we must schedule a recurring time - time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True) + # Because it is a maintenance task, we must schedule a recurring time + time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True) - # We create a monthly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH") + # We create a monthly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH") - # Specifically, the maintenance task must occur every 6 months - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6}) - """ - self.file = file - self.settings = {"parent": parent, "recurrence_type": recurrence_type} + # Specifically, the maintenance task must occur every 6 months + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6}) + """ + settings = {"parent": parent, "recurrence_type": recurrence_type} - def execute(self): - recurrence = self.file.createIfcRecurrencePattern( - self.settings["recurrence_type"] - ) + recurrence = file.createIfcRecurrencePattern(settings["recurrence_type"]) - if self.settings["parent"].is_a("IfcWorkTime"): - if ( - self.settings["parent"].RecurrencePattern - and len( - self.file.get_inverse(self.settings["parent"].RecurrencePattern) - ) - == 1 - ): - self.file.remove(self.settings["parent"].RecurrencePattern) - self.settings["parent"].RecurrencePattern = recurrence - elif self.settings["parent"].is_a("IfcTaskTimeRecurring"): - if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1: - self.file.remove(self.settings["parent"].Recurrence) - self.settings["parent"].Recurrence = recurrence - return recurrence + if settings["parent"].is_a("IfcWorkTime"): + if settings["parent"].RecurrencePattern and len(file.get_inverse(settings["parent"].RecurrencePattern)) == 1: + file.remove(settings["parent"].RecurrencePattern) + settings["parent"].RecurrencePattern = recurrence + elif settings["parent"].is_a("IfcTaskTimeRecurring"): + if len(file.get_inverse(settings["parent"].Recurrence)) == 1: + file.remove(settings["parent"].Recurrence) + settings["parent"].Recurrence = recurrence + return recurrence diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py index e1c1100760..ed5103758c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py @@ -20,119 +20,111 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__( - self, - file, - relating_process=None, - related_process=None, - sequence_type="FINISH_START", - ): - """Assign a sequential relationship between tasks +def assign_sequence( + file, + relating_process=None, + related_process=None, + sequence_type="FINISH_START", +) -> None: + """Assign a sequential relationship between tasks - Tasks in construction sequencing typically have sequence relationships - between them, indicating that one task must happen after another. This - is used to automatically compute new start and end dates and cascade - changes when dates are changed. This is also used to calculate critical - paths and floats. + Tasks in construction sequencing typically have sequence relationships + between them, indicating that one task must happen after another. This + is used to automatically compute new start and end dates and cascade + changes when dates are changed. This is also used to calculate critical + paths and floats. - There are four types of sequence relationships, known as finish to - start, finish to finish, start to start, and start to finish, sometimes - abbreviated as a (FS, FF, SS, and SF). The most common is the finish to - start relationship, indicating that the previous task must finish before - the next task can start. + There are four types of sequence relationships, known as finish to + start, finish to finish, start to start, and start to finish, sometimes + abbreviated as a (FS, FF, SS, and SF). The most common is the finish to + start relationship, indicating that the previous task must finish before + the next task can start. - You must not create cyclical task sequences. This makes the computer - unhappy. + You must not create cyclical task sequences. This makes the computer + unhappy. - Note that "previous" or "next" does not necessarily mean the task - chronologically happens before or after. They simply indicate the order - of the sequence relationship. For this reason, they are often called - predecessor and successor tasks in the planning profession. + Note that "previous" or "next" does not necessarily mean the task + chronologically happens before or after. They simply indicate the order + of the sequence relationship. For this reason, they are often called + predecessor and successor tasks in the planning profession. - :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance - :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance - :param sequence_type: Choose from FINISH_START, FINISH_FINISH, - START_START, or START_FINISH. - :return: The newly created IfcRelSequence - :rtype: ifcopenshell.entity_instance + :param relating_process: The previous / predecessor task. + :type relating_process: ifcopenshell.entity_instance + :param related_process: The next / successor task. + :type related_process: ifcopenshell.entity_instance + :param sequence_type: Choose from FINISH_START, FINISH_FINISH, + START_START, or START_FINISH. + :return: The newly created IfcRelSequence + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now the pour itself. It'll only take 1 day. - pour = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.3") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"}) + # Now the pour it It'll only take 1 day. + pour = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.3") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"}) - # Now let's say the formwork must finish before the reinforcement - # can start, and the reinforcement must finish before the pour can - # start. This is a typical finish to start relationship (FS). - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=reinforcement, related_process=pour) + # Now let's say the formwork must finish before the reinforcement + # can start, and the reinforcement must finish before the pour can + # start. This is a typical finish to start relationship (FS). + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=reinforcement, related_process=pour) - # Notice how we set all the scheduled start dates arbitrarily at - # 2000-01-01. This is because we can ask IfcOpenShell to - # automatically cascade the dates, starting from any task. This will - # update the reinforcement date to be 2000-01-03 and the pour date - # to be 2000-01-05. - ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_process": related_process, - "sequence_type": sequence_type, + # Notice how we set all the scheduled start dates arbitrarily at + # 2000-01-01. This is because we can ask IfcOpenShell to + # automatically cascade the dates, starting from any task. This will + # update the reinforcement date to be 2000-01-03 and the pour date + # to be 2000-01-05. + ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork) + """ + settings = { + "relating_process": relating_process, + "related_process": related_process, + "sequence_type": sequence_type, + } + + for rel in settings["related_process"].IsSuccessorFrom or []: + if rel.RelatingProcess == settings["relating_process"]: + return rel + rel = file.create_entity( + "IfcRelSequence", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatingProcess": settings["relating_process"], + "RelatedProcess": settings["related_process"], + "SequenceType": settings["sequence_type"], } - - def execute(self): - for rel in self.settings["related_process"].IsSuccessorFrom or []: - if rel.RelatingProcess == self.settings["relating_process"]: - return rel - rel = self.file.create_entity( - "IfcRelSequence", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatingProcess": self.settings["relating_process"], - "RelatedProcess": self.settings["related_process"], - "SequenceType": self.settings["sequence_type"], - } - ) - ifcopenshell.api.run( - "sequence.cascade_schedule", self.file, task=self.settings["relating_process"] - ) - return rel + ) + ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["relating_process"]) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index a1eaed71be..634f2af494 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -20,52 +20,49 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, work_schedule=None, work_plan=None): - """Assigns a work schedule to a work plan +def assign_workplan(file, work_schedule=None, work_plan=None) -> None: + """Assigns a work schedule to a work plan - Typically, work schedules would be assigned to a work plan at creation. - However you may also delay this and do it manually afterwards. + Typically, work schedules would be assigned to a work plan at creation. + However you may also delay this and do it manually afterwards. - :param work_schedule: The IfcWorkSchedule that will be assigned to the - work plan. - :type work_schedule: ifcopenshell.entity_instance - :param work_plan: The IfcWorkPlan for the schedule to be assigned to. - :type work_plan: ifcopenshell.entity_instance - :return: The IfcRelAggregates relationship - :rtype: ifcopenshell.entity_instance + :param work_schedule: The IfcWorkSchedule that will be assigned to the + work plan. + :type work_schedule: ifcopenshell.entity_instance + :param work_plan: The IfcWorkPlan for the schedule to be assigned to. + :type work_plan: ifcopenshell.entity_instance + :return: The IfcRelAggregates relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Alternatively, if you create a schedule without a work plan ... - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Alternatively, if you create a schedule without a work plan ... + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # ... you can assign the work plan afterwards. - ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan) - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "work_plan": work_plan} + # ... you can assign the work plan afterwards. + ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan) + """ + settings = {"work_schedule": work_schedule, "work_plan": work_plan} - def execute(self): - # TODO: this is an ambiguity by buildingSMART - # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_schedule"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - rel_aggregates = ifcopenshell.api.run( - "aggregate.assign_object", - self.file, - **{ - "products": [self.settings["work_schedule"]], - "relating_object": self.settings["work_plan"], - } - ) - return rel_aggregates + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_schedule"]], + relating_context=file.by_type("IfcContext")[0], + ) + rel_aggregates = ifcopenshell.api.run( + "aggregate.assign_object", + file, + **{ + "products": [settings["work_schedule"]], + "relating_object": settings["work_plan"], + } + ) + return rel_aggregates diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py index 6698ab85a2..22c9ec7dda 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py @@ -22,68 +22,71 @@ import ifcopenshell.util.date import ifcopenshell.util.element +def calculate_task_duration(file, task=None) -> None: + """Calculates the task duration based on resource usage + + If a task has labour or equipment resources assigned to it, its duration + may be parametrically derived from the scheduled work of the resource. + For example, a labour resource with scheduled work of 10 working days + and a resource utilisation of 200% (i.e. two labour teams) will imply + that the task duration is 5 working days. + + If this data is not available, such as if the task has no resources, + then nothing happens. + + :param task: The IfcTask to calculate the duration for. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") + + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") + + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) + + # Let's imagine we've used the resource for 10 days with a + # utilisation of 200%. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2}) + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Foundations", identification="A") + + # Assign our resource to the task. + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour) + + # Now we can calculate the task duration based on the resource. This + # will set task.TaskTime.ScheduleDuration to be P5D. + ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Calculates the task duration based on resource usage - - If a task has labour or equipment resources assigned to it, its duration - may be parametrically derived from the scheduled work of the resource. - For example, a labour resource with scheduled work of 10 working days - and a resource utilisation of 200% (i.e. two labour teams) will imply - that the task duration is 5 working days. - - If this data is not available, such as if the task has no resources, - then nothing happens. - - :param task: The IfcTask to calculate the duration for. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") - - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") - - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) - - # Let's imagine we've used the resource for 10 days with a - # utilisation of 200%. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2}) - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Foundations", identification="A") - - # Assign our resource to the task. - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour) - - # Now we can calculate the task duration based on the resource. This - # will set task.TaskTime.ScheduleDuration to be P5D. - ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.seconds_per_workday = self.calculate_seconds_per_workday() duration = self.calculate_max_resource_usage_duration() @@ -93,9 +96,7 @@ class Usecase: def calculate_seconds_per_workday(self): def get_work_schedule(task): for rel in task.HasAssignments or []: - if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a( - "IfcWorkSchedule" - ): + if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"): return rel.RelatingControl for rel in task.Nests or []: return get_work_schedule(rel.RelatingObject) @@ -111,9 +112,7 @@ class Usecase: or "WorkDayDuration" not in psets["Pset_WorkControlCommon"] ): return default_seconds_per_workday - work_day_duration = ifcopenshell.util.date.ifc2datetime( - psets["Pset_WorkControlCommon"]["WorkDayDuration"] - ) + work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"]) return work_day_duration.seconds def calculate_max_resource_usage_duration(self): @@ -133,23 +132,15 @@ class Usecase: if not resource.Usage or not resource.Usage.ScheduleWork: return schedule_usage = resource.Usage.ScheduleUsage or 1 - schedule_duration = ifcopenshell.util.date.ifc2datetime( - resource.Usage.ScheduleWork - ) + schedule_duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork) if is_hourly_work(resource.Usage.ScheduleWork): - schedule_seconds = ( - schedule_duration.days * 24 * 60 * 60 - ) + schedule_duration.seconds + schedule_seconds = (schedule_duration.days * 24 * 60 * 60) + schedule_duration.seconds else: partial_days = schedule_duration.seconds / (24 * 60 * 60) - schedule_seconds = ( - schedule_duration.days + partial_days - ) * self.seconds_per_workday + schedule_seconds = (schedule_duration.days + partial_days) * self.seconds_per_workday return math.ceil((schedule_seconds / self.seconds_per_workday) / schedule_usage) def set_task_duration(self, duration): if not self.settings["task"].TaskTime: - ifcopenshell.api.run( - "sequence.add_task_time", self.file, task=self.settings["task"] - ) + ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.settings["task"]) self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D" diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py index 2a720b9fa1..0a2aea7190 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py @@ -21,90 +21,93 @@ import ifcopenshell.util.date import ifcopenshell.util.sequence +def cascade_schedule(file, task=None) -> None: + """Cascades start and end dates of tasks based on durations + + Given a start task with a start date and duration, the end date, and the + start and end of all successor tasks with durations may be automatically + computed. + + Using this automatic computation is recommended is an alternative to + manually specifying dates. It is useful for doing edits and cascading + changes. + + Dates can only cascade from predecessor to successors, not backwards. + Cyclical relationships are invalid and will result in a recursion error + being raised. + + Note that there may be differences between how different planning + software calculate start and end dates. Some may consider Monday 5pm to + be equivalent to be Tuesday 8am, for instance. + + :param task: The start task to begin cascading from. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Define a convenience function to add a task chained to a predecessor + def add_task(model, name, predecessor, work_schedule): + # Add a construction task + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION") + + # Give it a time + task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) + + # Arbitrarily set the task's scheduled time duration to be 1 week + ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time, + attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"}) + + # If a predecessor exists, create a finish to start relationship + if predecessor: + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=predecessor, related_process=task) + + return task + + # Open an existing IFC4 model you have of a building + model = ifcopenshell.open("/path/to/existing/model.ifc") + + # Create a new construction schedule + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction") + + # Let's imagine a starting task for site establishment. + task = add_task(model, "Site establishment", None, schedule) + start_task = task + + # Get all our storeys sorted by elevation ascending. + storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s)) + + # For each storey ... + for storey in storeys: + + # Add a construction task to construct that storey, using our convenience function + task = add_task(model, f"Construct {storey.Name}", task, schedule) + + # Assign all the products in that storey to the task as construction outputs. + for product in get_decomposition(storey): + ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task) + + # Ask the computer to calculate all the dates for us from the start task. + # For example, if the first task started on the 1st of January and took a + # week, the next task will start on the 8th of January. This saves us + # manually doing date calculations. + ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task) + + # Calculate the critical path and floats. + ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Cascades start and end dates of tasks based on durations - - Given a start task with a start date and duration, the end date, and the - start and end of all successor tasks with durations may be automatically - computed. - - Using this automatic computation is recommended is an alternative to - manually specifying dates. It is useful for doing edits and cascading - changes. - - Dates can only cascade from predecessor to successors, not backwards. - Cyclical relationships are invalid and will result in a recursion error - being raised. - - Note that there may be differences between how different planning - software calculate start and end dates. Some may consider Monday 5pm to - be equivalent to be Tuesday 8am, for instance. - - :param task: The start task to begin cascading from. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Define a convenience function to add a task chained to a predecessor - def add_task(model, name, predecessor, work_schedule): - # Add a construction task - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION") - - # Give it a time - task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) - - # Arbitrarily set the task's scheduled time duration to be 1 week - ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time, - attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"}) - - # If a predecessor exists, create a finish to start relationship - if predecessor: - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=predecessor, related_process=task) - - return task - - # Open an existing IFC4 model you have of a building - model = ifcopenshell.open("/path/to/existing/model.ifc") - - # Create a new construction schedule - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction") - - # Let's imagine a starting task for site establishment. - task = add_task(model, "Site establishment", None, schedule) - start_task = task - - # Get all our storeys sorted by elevation ascending. - storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s)) - - # For each storey ... - for storey in storeys: - - # Add a construction task to construct that storey, using our convenience function - task = add_task(model, f"Construct {storey.Name}", task, schedule) - - # Assign all the products in that storey to the task as construction outputs. - for product in get_decomposition(storey): - ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task) - - # Ask the computer to calculate all the dates for us from the start task. - # For example, if the first task started on the 1st of January and took a - # week, the next task will start on the 8th of January. This saves us - # manually doing date calculations. - ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task) - - # Calculate the critical path and floats. - ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.calendar_cache = {} self.cascade_task(self.settings["task"], is_first_task=True) @@ -135,14 +138,10 @@ class Usecase: finishes = [] starts = [] - for rel in ifcopenshell.util.sequence.get_sequence_assignment( - task, "predecessor" - ): + for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor"): predecessor = rel.RelatingProcess predecessor_duration = ( - ifcopenshell.util.date.ifc2datetime( - predecessor.TaskTime.ScheduleDuration - ) + ifcopenshell.util.date.ifc2datetime(predecessor.TaskTime.ScheduleDuration) if predecessor.TaskTime and predecessor.TaskTime.ScheduleDuration else datetime.timedelta() ) @@ -154,14 +153,16 @@ class Usecase: duration_type = "WORKTIME" if rel.TimeLag: # updated to handle IfcRatioMeasure as a TimeLag value - days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days += ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType if days: starts.append( datetime.datetime.combine( - self.offset_date( - finish, days, duration_type, self.get_calendar(task) - ), + self.offset_date(finish, days, duration_type, self.get_calendar(task)), datetime.time(9), ) ) @@ -183,18 +184,14 @@ class Usecase: if not start: continue if rel.TimeLag: - days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days = ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType - starts.append( - self.offset_date( - start, days, duration_type, self.get_calendar(task) - ) - ) - starts.append( - self.offset_date( - start, days, duration_type, self.get_calendar(predecessor) - ) - ) + starts.append(self.offset_date(start, days, duration_type, self.get_calendar(task))) + starts.append(self.offset_date(start, days, duration_type, self.get_calendar(predecessor))) else: starts.append(start) elif rel.SequenceType == "FINISH_FINISH": @@ -202,18 +199,14 @@ class Usecase: if not finish: continue if rel.TimeLag: - days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days = ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType - finishes.append( - self.offset_date( - finish, days, duration_type, self.get_calendar(task) - ) - ) - finishes.append( - self.offset_date( - finish, days, duration_type, self.get_calendar(predecessor) - ) - ) + finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(task))) + finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(predecessor))) else: finishes.append(finish) elif rel.SequenceType == "START_FINISH": @@ -223,14 +216,16 @@ class Usecase: days = -1 duration_type = "WORKTIME" if rel.TimeLag: - days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days += ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType if days or rel.TimeLag: finishes.append( datetime.datetime.combine( - self.offset_date( - start, days, duration_type, self.get_calendar(task) - ), + self.offset_date(start, days, duration_type, self.get_calendar(task)), datetime.time(17), ) ) @@ -263,9 +258,7 @@ class Usecase: if task.TaskTime.ScheduleStart == start_ifc and not is_first_task: return task.TaskTime.ScheduleStart = start_ifc - task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc( - potential_finish, "IfcDateTime" - ) + task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(potential_finish, "IfcDateTime") else: finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime") if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task: @@ -328,15 +321,11 @@ class Usecase: def get_calendar(self, task): if task.id() not in self.calendar_cache: - self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar( - task - ) + self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(task) return self.calendar_cache[task.id()] def offset_date(self, date, days, duration_type, calendar): - return ifcopenshell.util.sequence.offset_date( - date, datetime.timedelta(days=days), duration_type, calendar - ) + return ifcopenshell.util.sequence.offset_date(date, datetime.timedelta(days=days), duration_type, calendar) def get_task_time_attribute(self, task, attribute): if task.TaskTime: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py index c0ebe4f72f..a06fe8d722 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py @@ -21,38 +21,41 @@ import ifcopenshell.util.system import ifcopenshell.util.element +def create_baseline(file, work_schedule=None, name=None) -> None: + """Creates a baseline for your Work Schedule + + Using a IfcWorkSchdule having PredefinedType=PLANNED, + We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE + and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline. + + The following relationships are also baselined: + + * Same Tasks & attributes + * Same Task Relationships + * Same Construction Resources + * Same Resource Relationships + + :param work_schedule: The planned work_schedule to baseline + :type work_schedule: ifcopenshell.entity_instance + :return: The baseline work_schedule + :rtype: ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a Work Schedule + planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01") + + # And now we have a baseline for our Work Schedule + baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"work_schedule": work_schedule, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, work_schedule=None, name=None): - """Creates a baseline for your Work Schedule - - Using a IfcWorkSchdule having PredefinedType=PLANNED, - We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE - and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline. - - The following relationships are also baselined: - - * Same Tasks & attributes - * Same Task Relationships - * Same Construction Resources - * Same Resource Relationships - - :param work_schedule: The planned work_schedule to baseline - :type work_schedule: ifcopenshell.entity_instance - :return: The baseline work_schedule - :rtype: ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a Work Schedule - planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01") - - # And now we have a baseline for our Work Schedule - baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1") - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "name": name} - def execute(self): result = self.create_baseline_work_schedule(self.settings["work_schedule"]) return result @@ -92,17 +95,13 @@ class Usecase: related_objects = list(referenced_by.RelatedObjects) related_objects.append(related_object) referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": referenced_by} - ) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by}) else: referenced_by = self.file.create_entity( "IfcRelDefinesByObject", **{ "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "RelatedObjects": [related_object], "RelatingObject": relating_object, } diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py index 91d5ed1b99..14016794cd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py @@ -22,33 +22,36 @@ import ifcopenshell.util.element import ifcopenshell.util.sequence +def duplicate_task(file, task=None) -> None: + """Duplicates a task in the project + + The following relationships are also duplicated: + + * The copy will have the same attributes and property sets as the original task + * The copy will be assigned to the parent task or work schedule + * The copy will have duplicated nested tasks + + :param task: The task to be duplicated + :type task: ifcopenshell.entity_instance + :return: The duplicated task or the list of duplicated tasks if the latter has children + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a task + original_task = Task(name="Design new feature", deadline="2023-03-01") + + # And now we have two + duplicated_task = project.duplicate_task(original_task) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Duplicates a task in the project - - The following relationships are also duplicated: - - * The copy will have the same attributes and property sets as the original task - * The copy will be assigned to the parent task or work schedule - * The copy will have duplicated nested tasks - - :param task: The task to be duplicated - :type task: ifcopenshell.entity_instance - :return: The duplicated task or the list of duplicated tasks if the latter has children - :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a task - original_task = Task(name="Design new feature", deadline="2023-03-01") - - # And now we have two - duplicated_task = project.duplicate_task(original_task) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.tracker = {"current": [], "duplicate": []} self.duplicate_task(self.settings["task"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py index f5779b058c..4b77daf9e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py @@ -20,79 +20,68 @@ import ifcopenshell.api import ifcopenshell.util.date -class Usecase: - def __init__(self, file, lag_time=None, attributes=None): - """Edits the attributes of an IfcLagTime +def edit_lag_time(file, lag_time=None, attributes=None) -> None: + """Edits the attributes of an IfcLagTime - For more information about the attributes and data types of an - IfcLagTime, consult the IFC documentation. + For more information about the attributes and data types of an + IfcLagTime, consult the IFC documentation. - :param lag_time: The IfcLagTime entity you want to edit - :type lag_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param lag_time: The IfcLagTime entity you want to edit + :type lag_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's say the formwork must finish before the reinforcement - # can start. This is a typical finish to start relationship (FS). - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) + # Now let's say the formwork must finish before the reinforcement + # can start. This is a typical finish to start relationship (FS). + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) - # Now typically there would be no lag time between formwork and - # reinforcement, but let's pretend that we had to allow 1 day gap - # for whatever reason. - lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") + # Now typically there would be no lag time between formwork and + # reinforcement, but let's pretend that we had to allow 1 day gap + # for whatever reason. + lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") - # Or, let's make it 2 days instead. - ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"}) - """ - self.file = file - self.settings = {"lag_time": lag_time, "attributes": attributes or {}} + # Or, let's make it 2 days instead. + ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"}) + """ + settings = {"lag_time": lag_time, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "LagValue" and value is not None: - if isinstance(value, float): - value = self.file.createIfcRatioMeasure(value) - else: - value = self.file.createIfcDuration( - ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - ) - setattr(self.settings["lag_time"], name, value) - for rel in [ - r - for r in self.file.get_inverse(self.settings["lag_time"]) - if r.is_a("IfcRelSequence") - ]: - ifcopenshell.api.run( - "sequence.cascade_schedule", self.file, task=rel.RelatedProcess - ) + for name, value in settings["attributes"].items(): + if name == "LagValue" and value is not None: + if isinstance(value, float): + value = file.createIfcRatioMeasure(value) + else: + value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")) + setattr(settings["lag_time"], name, value) + for rel in [r for r in file.get_inverse(settings["lag_time"]) if r.is_a("IfcRelSequence")]: + ifcopenshell.api.run("sequence.cascade_schedule", file, task=rel.RelatedProcess) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py index 21292233ad..2863102b3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -20,48 +20,45 @@ import ifcopenshell import ifcopenshell.util.sequence -class Usecase: - def __init__(self, file, recurrence_pattern=None, attributes=None): - """Edits the attributes of an IfcRecurrencePattern +def edit_recurrence_pattern(file, recurrence_pattern=None, attributes=None) -> None: + """Edits the attributes of an IfcRecurrencePattern - For more information about the attributes and data types of an - IfcRecurrencePattern, consult the IFC documentation. + For more information about the attributes and data types of an + IfcRecurrencePattern, consult the IFC documentation. - :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit - :type recurrence_pattern: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit + :type recurrence_pattern: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - """ - self.file = file - self.settings = { - "recurrence_pattern": recurrence_pattern, - "attributes": attributes or {}, - } + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + """ + settings = { + "recurrence_pattern": recurrence_pattern, + "attributes": attributes or {}, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["recurrence_pattern"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["recurrence_pattern"], name, value) - ifcopenshell.util.sequence.is_working_day.cache_clear() - ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() + ifcopenshell.util.sequence.is_working_day.cache_clear() + ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py index c563cb6990..bcbc521ef3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py @@ -20,55 +20,52 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, rel_sequence=None, attributes=None): - """Edits the attributes of an IfcRelSequence +def edit_sequence(file, rel_sequence=None, attributes=None) -> None: + """Edits the attributes of an IfcRelSequence - For more information about the attributes and data types of an - IfcRelSequence, consult the IFC documentation. + For more information about the attributes and data types of an + IfcRelSequence, consult the IFC documentation. - :param rel_sequence: The IfcRelSequence entity you want to edit - :type rel_sequence: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param rel_sequence: The IfcRelSequence entity you want to edit + :type rel_sequence: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=zone1, related_process=zone2) - # What if they both started at the same time? - ifcopenshell.api.run("sequence.edit_sequence", model, - rel_sequence=sequence, attributes={"SequenceType": "START_START"}) - """ - self.file = file - self.settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}} + # What if they both started at the same time? + ifcopenshell.api.run("sequence.edit_sequence", model, + rel_sequence=sequence, attributes={"SequenceType": "START_START"}) + """ + settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["rel_sequence"], name, value) - if "SequenceType" in self.settings["attributes"].keys(): - ifcopenshell.api.run( - "sequence.cascade_schedule", - self.file, - task=self.settings["rel_sequence"].RelatedProcess, - ) + for name, value in settings["attributes"].items(): + setattr(settings["rel_sequence"], name, value) + if "SequenceType" in settings["attributes"].keys(): + ifcopenshell.api.run( + "sequence.cascade_schedule", + file, + task=settings["rel_sequence"].RelatedProcess, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py index cbdaa18de0..d151926a69 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py @@ -17,39 +17,36 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, task=None, attributes=None): - """Edits the attributes of an IfcTask +def edit_task(file, task=None, attributes=None) -> None: + """Edits the attributes of an IfcTask - For more information about the attributes and data types of an - IfcTask, consult the IFC documentation. + For more information about the attributes and data types of an + IfcTask, consult the IFC documentation. - :param task: The IfcTask entity you want to edit - :type task: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param task: The IfcTask entity you want to edit + :type task: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the design milestones, and major - # project phases. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") + # Add a root task to represent the design milestones, and major + # project phases. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") - # Change the identification - ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"}) - """ - self.file = file - self.settings = {"task": task, "attributes": attributes or {}} + # Change the identification + ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"}) + """ + settings = {"task": task, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["task"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["task"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py index 10bec7909c..f81b2b1f90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py @@ -23,46 +23,48 @@ import ifcopenshell.util.sequence from typing import Any, Optional +def edit_task_time( + file: ifcopenshell.file, + task_time: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcTaskTime + + For more information about the attributes and data types of an + IfcTaskTime, consult the IFC documentation. + + :param task_time: The IfcTaskTime entity you want to edit + :type task_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Create a task to do formwork + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Formwork", identification="A") + + # Let's say it takes 2 days and starts on the 1st of January, 2000 + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task_time": task_time, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - task_time: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcTaskTime - - For more information about the attributes and data types of an - IfcTaskTime, consult the IFC documentation. - - :param task_time: The IfcTaskTime entity you want to edit - :type task_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Create a task to do formwork - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Formwork", identification="A") - - # Let's say it takes 2 days and starts on the 1st of January, 2000 - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - """ - self.file = file - self.settings = {"task_time": task_time, "attributes": attributes or {}} - - def execute(self) -> None: + def execute(self): self.task = self.get_task() self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task) @@ -73,17 +75,13 @@ class Usecase: ): del self.settings["attributes"]["ScheduleFinish"] - duration_type = self.settings["attributes"].get( - "DurationType", self.settings["task_time"].DurationType - ) + duration_type = self.settings["attributes"].get("DurationType", self.settings["task_time"].DurationType) finish = self.settings["attributes"].get("ScheduleFinish", None) if finish: if isinstance(finish, str): finish = datetime.datetime.fromisoformat(finish) self.settings["attributes"]["ScheduleFinish"] = datetime.datetime.combine( - ifcopenshell.util.sequence.get_soonest_working_day( - finish, duration_type, self.calendar - ), + ifcopenshell.util.sequence.get_soonest_working_day(finish, duration_type, self.calendar), datetime.time(17), ) start = self.settings["attributes"].get("ScheduleStart", None) @@ -91,9 +89,7 @@ class Usecase: if isinstance(start, str): start = datetime.datetime.fromisoformat(start) self.settings["attributes"]["ScheduleStart"] = datetime.datetime.combine( - ifcopenshell.util.sequence.get_soonest_working_day( - start, duration_type, self.calendar - ), + ifcopenshell.util.sequence.get_soonest_working_day(start, duration_type, self.calendar), datetime.time(9), ) @@ -101,11 +97,7 @@ class Usecase: if value is not None: if "Start" in name or "Finish" in name or name == "StatusTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif ( - name == "ScheduleDuration" - or name == "ActualDuration" - or name == "RemainingTime" - ): + elif name == "ScheduleDuration" or name == "ActualDuration" or name == "RemainingTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") setattr(self.settings["task_time"], name, value) @@ -115,15 +107,9 @@ class Usecase: and self.settings["task_time"].ScheduleStart ): self.calculate_finish() - elif ( - self.settings["attributes"].get("ScheduleStart", None) - and self.settings["task_time"].ScheduleDuration - ): + elif self.settings["attributes"].get("ScheduleStart", None) and self.settings["task_time"].ScheduleDuration: self.calculate_finish() - elif ( - self.settings["attributes"].get("ScheduleFinish", None) - and self.settings["task_time"].ScheduleStart - ): + elif self.settings["attributes"].get("ScheduleFinish", None) and self.settings["task_time"].ScheduleStart: self.calculate_duration() if self.settings["task_time"].ScheduleDuration and ( @@ -137,57 +123,36 @@ class Usecase: def calculate_finish(self): finish = ifcopenshell.util.sequence.get_start_or_finish_date( - ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleStart - ), - ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleDuration - ), + ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart), + ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleDuration), self.settings["task_time"].DurationType, self.calendar, date_type="FINISH", ) - self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc( - finish, "IfcDateTime" - ) + self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime") def calculate_duration(self): - start = ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleStart - ) - finish = ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleFinish - ) + start = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart) + finish = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleFinish) current_date = datetime.date(start.year, start.month, start.day) finish_date = datetime.date(finish.year, finish.month, finish.day) duration = datetime.timedelta(days=1) while current_date < finish_date: - if ( - self.settings["task_time"].DurationType == "ELAPSEDTIME" - or not self.calendar - ): + if self.settings["task_time"].DurationType == "ELAPSEDTIME" or not self.calendar: duration += datetime.timedelta(days=1) elif ifcopenshell.util.sequence.is_working_day(current_date, self.calendar): duration += datetime.timedelta(days=1) current_date += datetime.timedelta(days=1) - self.settings[ - "task_time" - ].ScheduleDuration = ifcopenshell.util.date.datetime2ifc( - duration, "IfcDuration" - ) + self.settings["task_time"].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration, "IfcDuration") def get_task(self) -> ifcopenshell.entity_instance: - return next( - e - for e in self.file.get_inverse(self.settings["task_time"]) - if e.is_a("IfcTask") - ) + return next(e for e in self.file.get_inverse(self.settings["task_time"]) if e.is_a("IfcTask")) def handle_resource_calculation(self): resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False) for resource in resources: if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleWork"): ifcopenshell.api.run("resource.calculate_resource_usage", self.file, resource=resource) - #TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated. + # TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated. # elif ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"): # ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=resource) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py index 12ce1e15d1..4efb35da84 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_calendar=None, attributes=None): - """Edits the attributes of an IfcWorkCalendar +def edit_work_calendar(file, work_calendar=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkCalendar - For more information about the attributes and data types of an - IfcWorkCalendar, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkCalendar, consult the IFC documentation. - :param work_calendar: The IfcWorkCalendar entity you want to edit - :type work_calendar: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_calendar: The IfcWorkCalendar entity you want to edit + :type work_calendar: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_calendar", model, - work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"}) - """ - self.file = file - self.settings = {"work_calendar": work_calendar, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_calendar", model, + work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"}) + """ + settings = {"work_calendar": work_calendar, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["work_calendar"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["work_calendar"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py index 669ef0193c..e2bbcae33f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py @@ -19,39 +19,36 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, work_plan=None, attributes=None): - """Edits the attributes of an IfcWorkPlan +def edit_work_plan(file, work_plan=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkPlan - For more information about the attributes and data types of an - IfcWorkPlan, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkPlan, consult the IFC documentation. - :param work_plan: The IfcWorkPlan entity you want to edit - :type work_plan: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_plan: The IfcWorkPlan entity you want to edit + :type work_plan: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_plan", model, - work_plan=work_plan, attributes={"Description": "Construction of phase 1"}) - """ - self.file = file - self.settings = {"work_plan": work_plan, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_plan", model, + work_plan=work_plan, attributes={"Description": "Construction of phase 1"}) + """ + settings = {"work_plan": work_plan, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if value: - if "Date" in name or "Time" in name: - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif name == "Duration" or name == "TotalFloat": - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["work_plan"], name, value) + for name, value in settings["attributes"].items(): + if value: + if "Date" in name or "Time" in name: + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") + elif name == "Duration" or name == "TotalFloat": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") + setattr(settings["work_plan"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py index cd7ca163b2..49e6b053ac 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py @@ -19,43 +19,40 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, work_schedule=None, attributes=None): - """Edits the attributes of an IfcWorkSchedule +def edit_work_schedule(file, work_schedule=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkSchedule - For more information about the attributes and data types of an - IfcWorkSchedule, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkSchedule, consult the IFC documentation. - :param work_schedule: The IfcWorkSchedule entity you want to edit - :type work_schedule: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_schedule: The IfcWorkSchedule entity you want to edit + :type work_schedule: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_schedule", model, - work_schedule=work_schedule, attributes={"Description": "3 crane design option"}) - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_schedule", model, + work_schedule=work_schedule, attributes={"Description": "3 crane design option"}) + """ + settings = {"work_schedule": work_schedule, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if value: - if "Date" in name or "Time" in name: - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif name == "Duration" or name == "TotalFloat": - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["work_schedule"], name, value) + for name, value in settings["attributes"].items(): + if value: + if "Date" in name or "Time" in name: + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") + elif name == "Duration" or name == "TotalFloat": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") + setattr(settings["work_schedule"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py index d62c3a5357..ac0a05dad0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -20,54 +20,50 @@ import ifcopenshell.util.date from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - work_time: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcWorkTime +def edit_work_time( + file: ifcopenshell.file, + work_time: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcWorkTime - For more information about the attributes and data types of an - IfcWorkTime, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkTime, consult the IFC documentation. - :param work_time: The IfcWorkTime entity you want to edit - :type work_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_time: The IfcWorkTime entity you want to edit + :type work_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # If we don't specify any recurring time periods in our work time, - # we need to specify a start and end date of the work time. It - # starts at 0:00 on the start date and 24:00 at the end date. - ifcopenshell.api.run("sequence.edit_work_time", model, - work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"}) - """ - self.file = file - self.settings = {"work_time": work_time, "attributes": attributes or {}} + # If we don't specify any recurring time periods in our work time, + # we need to specify a start and end date of the work time. It + # starts at 0:00 on the start date and 24:00 at the end date. + ifcopenshell.api.run("sequence.edit_work_time", model, + work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"}) + """ + settings = {"work_time": work_time, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - if name in ("Start", "StartDate"): - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") - # 4 IfcWorktime Start - self.settings["work_time"][4] = value - elif name in ("Finish", "FinishDate"): - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") - # 5 IfcWorktime Finish - self.settings["work_time"][5] = value - else: - setattr(self.settings["work_time"], name, value) + for name, value in settings["attributes"].items(): + if name in ("Start", "StartDate"): + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") + # 4 IfcWorktime Start + settings["work_time"][4] = value + elif name in ("Finish", "FinishDate"): + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") + # 5 IfcWorktime Finish + settings["work_time"][5] = value + else: + setattr(settings["work_time"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py index eb8af300d1..df402c31ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py @@ -19,61 +19,58 @@ import ifcopenshell -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Gets the related products being output by a task +def get_related_products(file, relating_product=None, related_object=None) -> None: + """Gets the related products being output by a task - This API function will be removed in the future and migrated to a - utility module. + This API function will be removed in the future and migrated to a + utility module. - :param relating_product: One of the products already output by the task. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcTask that you want to get all the related - products for. - :type related_object: ifcopenshell.entity_instance - :return: A set of IfcProducts output by the IfcTask. - :rtype: set[ifcopenshell.entity_instance] + :param relating_product: One of the products already output by the task. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcTask that you want to get all the related + products for. + :type related_object: ifcopenshell.entity_instance + :return: A set of IfcProducts output by the IfcTask. + :rtype: set[ifcopenshell.entity_instance] - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) - # This will give us a set with that wall in it. - products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # This will give us a set with that wall in it. + products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - products = set() - related_object = None - if self.settings["related_object"]: - related_object = self.settings["related_object"] - elif self.settings["relating_product"]: - for reference in self.settings["relating_product"].ReferencedBy: - if reference.is_a("IfcRelAssignsToProduct"): - related_object = reference.RelatedObjects[0] - if related_object: - assignments = self.settings["related_object"].HasAssignments - for assignment in assignments: - if assignment.is_a("IfcRelAssignsToProduct"): - products.add(assignment.RelatingProduct.id()) - return products + products = set() + related_object = None + if settings["related_object"]: + related_object = settings["related_object"] + elif settings["relating_product"]: + for reference in settings["relating_product"].ReferencedBy: + if reference.is_a("IfcRelAssignsToProduct"): + related_object = reference.RelatedObjects[0] + if related_object: + assignments = settings["related_object"].HasAssignments + for assignment in assignments: + if assignment.is_a("IfcRelAssignsToProduct"): + products.add(assignment.RelatingProduct.id()) + return products diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py index da07337f7b..d54bf01579 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py @@ -23,35 +23,38 @@ import ifcopenshell.util.date import ifcopenshell.util.sequence +def recalculate_schedule(file, work_schedule=None) -> None: + """Calculate the critical path and floats for a work schedule + + This implements critical path analysis, using the forward pass and + backward pass method. When run, any tasks that have no float will be + marked as critical, and both the total and free floats will be + populated for all task times. + + Cyclical relationships are detected and will result in a recursion + error. + + :param work_schedule: The IfcWorkSchedule to perform the calculation on. + :type work_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # See the example for ifcopenshell.api.sequence.cascade_schedule for + # details of how to set up a basic set of tasks and calculate the + # critical path. Typically cascade_schedule is run prior to ensure + # that dates are correct. + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"work_schedule": work_schedule} + return usecase.execute() + + class Usecase: - def __init__(self, file, work_schedule=None): - """Calculate the critical path and floats for a work schedule - - This implements critical path analysis, using the forward pass and - backward pass method. When run, any tasks that have no float will be - marked as critical, and both the total and free floats will be - populated for all task times. - - Cyclical relationships are detected and will result in a recursion - error. - - :param work_schedule: The IfcWorkSchedule to perform the calculation on. - :type work_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # See the example for ifcopenshell.api.sequence.cascade_schedule for - # details of how to set up a basic set of tasks and calculate the - # critical path. Typically cascade_schedule is run prior to ensure - # that dates are correct. - """ - self.file = file - self.settings = {"work_schedule": work_schedule} - def execute(self): # The method implemented is the same as shown here: # https://www.youtube.com/watch?v=qTErIV6OqLg @@ -84,9 +87,7 @@ class Usecase: break # We have an infinite loop due to a cyclic graph if is_cyclic: - raise RecursionError( - "Task graph is cyclic and so critical path method cannot be performed." - ) + raise RecursionError("Task graph is cyclic and so critical path method cannot be performed.") return self.pending_nodes = set(self.g.nodes) @@ -112,9 +113,7 @@ class Usecase: self.g = nx.DiGraph() self.edges = [] self.g.add_node("start", duration=0, duration_type="ELAPSEDTIME", calendar=None) - self.g.add_node( - "finish", duration=0, duration_type="ELAPSEDTIME", calendar=None - ) + self.g.add_node("finish", duration=0, duration_type="ELAPSEDTIME", calendar=None) for rel in self.settings["work_schedule"].Controls: for related_object in rel.RelatedObjects: if not related_object.is_a("IfcTask"): @@ -129,9 +128,7 @@ class Usecase: return if task.TaskTime and task.TaskTime.ScheduleDuration: - duration = ifcopenshell.util.date.ifc2datetime( - task.TaskTime.ScheduleDuration - ).days + duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration).days duration_type = task.TaskTime.DurationType else: duration = 0 @@ -150,11 +147,11 @@ class Usecase: rel.RelatingProcess.id(), task.id(), { - "lag_time": 0 - if not rel.TimeLag - else ifcopenshell.util.date.ifc2datetime( - rel.TimeLag.LagValue.wrappedValue - ).days, + "lag_time": ( + 0 + if not rel.TimeLag + else ifcopenshell.util.date.ifc2datetime(rel.TimeLag.LagValue.wrappedValue).days + ), "type": self.sequence_type_map[rel.SequenceType], }, ) @@ -162,16 +159,20 @@ class Usecase: ] ) - predecessor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")] - successor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")] + predecessor_types = [ + rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor") + ] + successor_types = [ + rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor") + ] if not predecessor_types: self.edges.append(("start", task.id(), {"lag_time": 0, "type": "FS"})) if task.TaskTime and task.TaskTime.ScheduleStart: - self.start_dates.append( - ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) - ) - self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) # we assume this task is constrained to start on this date + self.start_dates.append(ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart)) + self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime( + task.TaskTime.ScheduleStart + ) # we assume this task is constrained to start on this date if not successor_types: self.edges.append((task.id(), "finish", {"lag_time": 0, "type": "FF"})) @@ -188,25 +189,13 @@ class Usecase: self.file, task_time=task.TaskTime, attributes={ - "FreeFloat": ifcopenshell.util.date.datetime2ifc( - data["free_float"], "IfcDuration" - ), - "TotalFloat": ifcopenshell.util.date.datetime2ifc( - data["total_float"], "IfcDuration" - ), + "FreeFloat": ifcopenshell.util.date.datetime2ifc(data["free_float"], "IfcDuration"), + "TotalFloat": ifcopenshell.util.date.datetime2ifc(data["total_float"], "IfcDuration"), "IsCritical": data["total_float"].days == 0, - "EarlyStart": ifcopenshell.util.date.datetime2ifc( - data["early_start"], "IfcDateTime" - ), - "EarlyFinish": ifcopenshell.util.date.datetime2ifc( - data["early_finish"], "IfcDateTime" - ), - "LateStart": ifcopenshell.util.date.datetime2ifc( - data["late_start"], "IfcDateTime" - ), - "LateFinish": ifcopenshell.util.date.datetime2ifc( - data["late_finish"], "IfcDateTime" - ), + "EarlyStart": ifcopenshell.util.date.datetime2ifc(data["early_start"], "IfcDateTime"), + "EarlyFinish": ifcopenshell.util.date.datetime2ifc(data["early_finish"], "IfcDateTime"), + "LateStart": ifcopenshell.util.date.datetime2ifc(data["late_start"], "IfcDateTime"), + "LateFinish": ifcopenshell.util.date.datetime2ifc(data["late_finish"], "IfcDateTime"), }, ) @@ -246,11 +235,7 @@ class Usecase: if edge["lag_time"]: days += edge["lag_time"] if days: - starts.append( - datetime.datetime.combine( - self.offset_date(finish, days, data), datetime.time(9) - ) - ) + starts.append(datetime.datetime.combine(self.offset_date(finish, days, data), datetime.time(9))) starts.append( datetime.datetime.combine( self.offset_date(finish, days, predecessor_data), @@ -265,9 +250,7 @@ class Usecase: return if edge["lag_time"]: starts.append(self.offset_date(start, edge["lag_time"], data)) - starts.append( - self.offset_date(start, edge["lag_time"], predecessor_data) - ) + starts.append(self.offset_date(start, edge["lag_time"], predecessor_data)) else: starts.append(start) elif edge["type"] == "FF": @@ -275,12 +258,8 @@ class Usecase: if finish is None: return if edge["lag_time"]: - finishes.append( - self.offset_date(finish, edge["lag_time"], data) - ) - finishes.append( - self.offset_date(finish, edge["lag_time"], predecessor_data) - ) + finishes.append(self.offset_date(finish, edge["lag_time"], data)) + finishes.append(self.offset_date(finish, edge["lag_time"], predecessor_data)) else: finishes.append(finish) elif edge["type"] == "SF": @@ -292,9 +271,7 @@ class Usecase: days += edge["lag_time"] if days or edge["lag_time"]: finishes.append( - datetime.datetime.combine( - self.offset_date(start, days, data), datetime.time(17) - ) + datetime.datetime.combine(self.offset_date(start, days, data), datetime.time(17)) ) finishes.append( datetime.datetime.combine( @@ -317,9 +294,7 @@ class Usecase: if potential_finish > data["early_finish"]: data["early_finish"] = potential_finish else: - data[ - "early_start" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + data["early_start"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["early_finish"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -375,9 +350,7 @@ class Usecase: days += edge["lag_time"] if days or edge["lag_time"]: finishes.append( - datetime.datetime.combine( - self.offset_date(start, -days, data), datetime.time(17) - ) + datetime.datetime.combine(self.offset_date(start, -days, data), datetime.time(17)) ) finishes.append( datetime.datetime.combine( @@ -402,9 +375,7 @@ class Usecase: return if edge["lag_time"]: starts.append(self.offset_date(start, -edge["lag_time"], data)) - starts.append( - self.offset_date(start, -edge["lag_time"], successor_data) - ) + starts.append(self.offset_date(start, -edge["lag_time"], successor_data)) else: starts.append(start) free_floats.append( @@ -421,12 +392,8 @@ class Usecase: if finish is None: return if edge["lag_time"]: - finishes.append( - self.offset_date(finish, -edge["lag_time"], data) - ) - finishes.append( - self.offset_date(finish, -edge["lag_time"], successor_data) - ) + finishes.append(self.offset_date(finish, -edge["lag_time"], data)) + finishes.append(self.offset_date(finish, -edge["lag_time"], successor_data)) else: finishes.append(finish) free_floats.append( @@ -447,9 +414,7 @@ class Usecase: days += edge["lag_time"] if days: starts.append( - datetime.datetime.combine( - self.offset_date(finish, -days, data), datetime.time(9) - ) + datetime.datetime.combine(self.offset_date(finish, -days, data), datetime.time(9)) ) starts.append( datetime.datetime.combine( @@ -471,13 +436,8 @@ class Usecase: if starts and finishes: data["late_start"] = min(starts) data["late_finish"] = min(finishes) - if ( - self.offset_date(data["late_start"], data["duration"], data) - < data["late_finish"] - ): - data[ - "late_finish" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + if self.offset_date(data["late_start"], data["duration"], data) < data["late_finish"]: + data["late_finish"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["late_start"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -485,9 +445,7 @@ class Usecase: date_type="FINISH", ) else: - data[ - "late_start" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + data["late_start"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["late_finish"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -528,9 +486,7 @@ class Usecase: data["total_float"] = data["late_finish"] - data["early_finish"] # If the float is within the span of a single day, it may show as a 8 hours if data["total_float"].seconds == 60 * 60 * 8: - data["total_float"] = datetime.timedelta( - days=data["total_float"].days + 1 - ) + data["total_float"] = datetime.timedelta(days=data["total_float"].days + 1) data["free_float"] = min(free_floats) if free_floats else None # If the float is within the span of a single day, it may show as a 8 hours diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py index 6b7fac4f75..d49da8324e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py @@ -21,124 +21,121 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, task=None): - """Removes a task +def remove_task(file, task=None) -> None: + """Removes a task - All subtasks are also removed recursively. Any relationships such as - sequences or controls are also removed. + All subtasks are also removed recursively. Any relationships such as + sequences or controls are also removed. - :param task: The IfcTask to remove. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None + :param task: The IfcTask to remove. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - design = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + design = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Ah, let's delete the design section, who needs it anyway we'll - # just fix it on site. - ifcopenshell.api.run("sequence.remove_task", model, task=design) - """ - self.file = file - self.settings = {"task": task} + # Ah, let's delete the design section, who needs it anyway we'll + # just fix it on site. + ifcopenshell.api.run("sequence.remove_task", model, task=design) + """ + settings = {"task": task} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["task"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["task"].TaskTime: - self.file.remove(self.settings["task"].TaskTime) - for inverse in self.file.get_inverse(self.settings["task"]): - if inverse.is_a("IfcRelSequence"): + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["task"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["task"].TaskTime: + file.remove(settings["task"].TaskTime) + for inverse in file.get_inverse(settings["task"]): + if inverse.is_a("IfcRelSequence"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["task"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run("sequence.remove_task", file, task=related_object) + elif not inverse.RelatedObjects: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["task"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object) - elif not inverse.RelatedObjects: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif self.settings["task"] in inverse.RelatedObjects: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - if not related_objects: - self.file.remove(inverse) - else: - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToControl"): - if inverse.RelatingControl == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif settings["task"] in inverse.RelatedObjects: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + if not related_objects: + file.remove(inverse) else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["task"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToProcess"): - if inverse.RelatingProcess == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToProduct"): - if inverse.RelatingProduct == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToObject"): - if inverse.RelatingObject == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToProcess"): + elif inverse.is_a("IfcRelAssignsToControl"): + if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["task"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToProcess"): + if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToProduct"): + if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToObject"): + if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToProcess"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - history = self.settings["task"].OwnerHistory - self.file.remove(self.settings["task"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["task"].OwnerHistory + file.remove(settings["task"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py index 32effdf4ac..672606c421 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py @@ -19,45 +19,42 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, time_period=None): - """Removes a time period +def remove_time_period(file, time_period=None) -> None: + """Removes a time period - :param time_period: The IfcTimePeriod to remove. - :type time_period: ifcopenshell.entity_instance - :return: None - :rtype: None + :param time_period: The IfcTimePeriod to remove. + :type time_period: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # The morning work session, lunch, then the afternoon work session. - morning = ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="12:00") - afternoon = ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="13:00", end_time="17:00") + # The morning work session, lunch, then the afternoon work session. + morning = ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="12:00") + afternoon = ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="13:00", end_time="17:00") - # Let's take the afternoon off! - ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon) - """ - self.file = file - self.settings = {"time_period": time_period} + # Let's take the afternoon off! + ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon) + """ + settings = {"time_period": time_period} - def execute(self): - self.file.remove(self.settings["time_period"]) + file.remove(settings["time_period"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index e233bef26d..22a362c42d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -20,49 +20,46 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_calendar=None): - """Removes a work calendar +def remove_work_calendar(file, work_calendar=None) -> None: + """Removes a work calendar - All relationships are also removed, such as if a task is set to use that - calendar. + All relationships are also removed, such as if a task is set to use that + calendar. - :param work_calendar: The IfcWorkCalendar to remove - :type work_calendar: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_calendar: The IfcWorkCalendar to remove + :type work_calendar: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar) - """ - self.file = file - self.settings = {"work_calendar": work_calendar} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar) + """ + settings = {"work_calendar": work_calendar} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_calendar"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["work_calendar"].Controls: - for rel in self.settings["work_calendar"].Controls: - for related_object in rel.RelatedObjects: - ifcopenshell.api.run( - "control.unassign_control", - self.file, - relating_control=self.settings["work_calendar"], - related_object=related_object, - ) - history = self.settings["work_calendar"].OwnerHistory - self.file.remove(self.settings["work_calendar"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_calendar"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["work_calendar"].Controls: + for rel in settings["work_calendar"].Controls: + for related_object in rel.RelatedObjects: + ifcopenshell.api.run( + "control.unassign_control", + file, + relating_control=settings["work_calendar"], + related_object=related_object, + ) + history = settings["work_calendar"].OwnerHistory + file.remove(settings["work_calendar"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py index bbd631829d..28675fe6a5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py @@ -20,40 +20,37 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_plan=None): - """Removes a work plan +def remove_work_plan(file, work_plan=None) -> None: + """Removes a work plan - Note that schedules that are grouped under the work plan are not - removed. + Note that schedules that are grouped under the work plan are not + removed. - :param work_plan: The IfcWorkPlan to remove. - :type work_plan: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_plan: The IfcWorkPlan to remove. + :type work_plan: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan) - """ - self.file = file - self.settings = {"work_plan": work_plan} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan) + """ + settings = {"work_plan": work_plan} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_plan"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - history = self.settings["work_plan"].OwnerHistory - self.file.remove(self.settings["work_plan"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_plan"]], + relating_context=file.by_type("IfcContext")[0], + ) + history = settings["work_plan"].OwnerHistory + file.remove(settings["work_plan"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py index 66b69c8804..ec06a9146b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py @@ -21,69 +21,66 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_schedule=None): - """Removes a work schedule +def remove_work_schedule(file, work_schedule=None) -> None: + """Removes a work schedule - All tasks in the work schedule are also removed recursively. + All tasks in the work schedule are also removed recursively. - :param work_schedule: The IfcWorkSchedule to remove. - :type work_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_schedule: The IfcWorkSchedule to remove. + :type work_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule) - """ - self.file = file - self.settings = {"work_schedule": work_schedule} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule) + """ + settings = {"work_schedule": work_schedule} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_schedule"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["work_schedule"].Declares: - for rel in self.settings["work_schedule"].Declares: - for work_schedule in rel.RelatedObjects: - ifcopenshell.api.run( - "sequence.remove_work_schedule", - self.file, - work_schedule=work_schedule, - ) - for inverse in self.file.get_inverse(self.settings["work_schedule"]): - if inverse.is_a("IfcRelDefinesByObject"): - if inverse.RelatingObject == self.settings["work_schedule"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["work_schedule"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToControl"): - [ - ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object) - for related_object in inverse.RelatedObjects - if related_object.is_a("IfcTask") - ] + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_schedule"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["work_schedule"].Declares: + for rel in settings["work_schedule"].Declares: + for work_schedule in rel.RelatedObjects: + ifcopenshell.api.run( + "sequence.remove_work_schedule", + file, + work_schedule=work_schedule, + ) + for inverse in file.get_inverse(settings["work_schedule"]): + if inverse.is_a("IfcRelDefinesByObject"): + if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["work_schedule"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToControl"): + [ + ifcopenshell.api.run("sequence.remove_task", file, task=related_object) + for related_object in inverse.RelatedObjects + if related_object.is_a("IfcTask") + ] - history = self.settings["work_schedule"].OwnerHistory - self.file.remove(self.settings["work_schedule"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["work_schedule"].OwnerHistory + file.remove(settings["work_schedule"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py index 3898e3655a..ab4587ce6a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_time=None): - """Removes a work time +def remove_work_time(file, work_time=None) -> None: + """Removes a work time - :param work_time: The IfcWorkTime to remove. - :type work_time: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_time: The IfcWorkTime to remove. + :type work_time: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time) - """ - self.file = file - self.settings = {"work_time": work_time} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time) + """ + settings = {"work_time": work_time} - def execute(self): - self.file.remove(self.settings["work_time"]) + file.remove(settings["work_time"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py index cca278da44..cac8f95372 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py @@ -19,57 +19,54 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, rel_sequence=None): - """Removes any lag time in a sequence +def unassign_lag_time(file, rel_sequence=None) -> None: + """Removes any lag time in a sequence - The schedule is cascaded afterwards. + The schedule is cascaded afterwards. - :param rel_sequence: The sequence to remove the lag time from. - :type rel_sequence: ifcopenshell.entity_instance - :return: None - :rtype: None + :param rel_sequence: The sequence to remove the lag time from. + :type rel_sequence: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=zone1, related_process=zone2) - # What if you had to wait 1 week before you could start zone 2? - ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W") + # What if you had to wait 1 week before you could start zone 2? + ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W") - # What if you didn't? - ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence) - """ - self.file = file - self.settings = { - "rel_sequence": rel_sequence, - } + # What if you didn't? + ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence) + """ + settings = { + "rel_sequence": rel_sequence, + } - def execute(self): - if len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1: - self.file.remove(self.settings["rel_sequence"].TimeLag) - else: - self.settings["rel_sequence"].TimeLag = None - ifcopenshell.api.run( - "sequence.cascade_schedule", - self.file, - task=self.settings["rel_sequence"].RelatedProcess, - ) + if len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1: + file.remove(settings["rel_sequence"].TimeLag) + else: + settings["rel_sequence"].TimeLag = None + ifcopenshell.api.run( + "sequence.cascade_schedule", + file, + task=settings["rel_sequence"].RelatedProcess, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py index dfc12068d3..f7e141afc1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py @@ -21,59 +21,56 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_process=None, related_object=None): - """Unassigns a process and object relationship +def unassign_process(file, relating_process=None, related_object=None) -> None: + """Unassigns a process and object relationship - See ifcopenshell.api.sequence.assign_process for details. + See ifcopenshell.api.sequence.assign_process for details. - :param relating_process: The IfcTask in the relationship. - :type relating_process: ifcopenshell.entity_instance - :param related_object: The related object. - :type related_object: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_process: The IfcTask in the relationship. + :type relating_process: ifcopenshell.entity_instance + :param related_object: The related object. + :type related_object: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's demolish that wall! - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) + # Let's demolish that wall! + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) - # Change our mind. - ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_object": related_object, - } + # Change our mind. + ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall) + """ + settings = { + "relating_process": relating_process, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != self.settings["relating_process"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py index 31d9edb0e1..23f9281c95 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py @@ -21,59 +21,56 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Unassigns a product and object relationship +def unassign_product(file, relating_product=None, related_object=None) -> None: + """Unassigns a product and object relationship - See ifcopenshell.api.sequence.assign_product for details. + See ifcopenshell.api.sequence.assign_product for details. - :param relating_product: The IfcProduct in the relationship. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcTask in the relationship. - :type related_object: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_product: The IfcProduct in the relationship. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcTask in the relationship. + :type related_object: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) - # Change our mind. - ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Change our mind. + ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py index fc74c69a95..46c99207f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, recurrence_pattern=None): - """Unassigns a recurrence pattern +def unassign_recurrence_pattern(file, recurrence_pattern=None) -> None: + """Unassigns a recurrence pattern - Note that a recurring task time must have a recurrence pattern, so if - you remove it, be sure to clean up after yourself. + Note that a recurring task time must have a recurrence pattern, so if + you remove it, be sure to clean up after your - :param recurrence_pattern: The IfcRecurrencePattern to remove. - :type recurrence_pattern: ifcopenshell.entity_instance - :return: None - :rtype: None + :param recurrence_pattern: The IfcRecurrencePattern to remove. + :type recurrence_pattern: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # Change our mind, let's just maintain it whenever we feel like it. - ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern) - """ - self.file = file - self.settings = {"recurrence_pattern": recurrence_pattern} + # Change our mind, let's just maintain it whenever we feel like it. + ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern) + """ + settings = {"recurrence_pattern": recurrence_pattern} - def execute(self): - for time_period in self.settings["recurrence_pattern"].TimePeriods or []: - self.file.remove(time_period) - self.file.remove(self.settings["recurrence_pattern"]) + for time_period in settings["recurrence_pattern"].TimePeriods or []: + file.remove(time_period) + file.remove(settings["recurrence_pattern"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py index c7286909bd..f10b11f893 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py @@ -21,53 +21,50 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_process=None, related_process=None): - """Removes a sequence relationship between tasks +def unassign_sequence(file, relating_process=None, related_process=None) -> None: + """Removes a sequence relationship between tasks - :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance - :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_process: The previous / predecessor task. + :type relating_process: ifcopenshell.entity_instance + :param related_process: The next / successor task. + :type related_process: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2) - # Let's make them unrelated - ifcopenshell.api.run("sequence.unassign_sequence", model, - relating_process=zone1, related_process=zone2) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_process": related_process, - } + # Let's make them unrelated + ifcopenshell.api.run("sequence.unassign_sequence", model, + relating_process=zone1, related_process=zone2) + """ + settings = { + "relating_process": relating_process, + "related_process": related_process, + } - def execute(self): - for rel in self.settings["related_process"].IsSuccessorFrom or []: - if rel.RelatingProcess == self.settings["relating_process"]: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=self.settings["related_process"]) + for rel in settings["related_process"].IsSuccessorFrom or []: + if rel.RelatingProcess == settings["relating_process"]: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["related_process"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py index e0caddbe3c..22891f5c83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_container import assign_container +from .dereference_structure import dereference_structure +from .reference_structure import reference_structure +from .unassign_container import unassign_container diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py index 9edf7ccba8..298fe3dfdb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py @@ -23,163 +23,159 @@ import ifcopenshell.util.placement from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Assigns products to be contained hierarchically in a space +def assign_container( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns products to be contained hierarchically in a space - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. See - ifcopenshell.api.aggregate.assign_object for more details about - aggregation. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. See + ifcopenshell.api.aggregate.assign_object for more details about + aggregation. - The IfcProject will be "decomposed" into spatial structure elements. - These are virtual spaces like stes, buildings, storeys, and spaces (i.e. - rooms). You can't physically touch these spaces, but you can touch the - products contained within these spaces. + The IfcProject will be "decomposed" into spatial structure elements. + These are virtual spaces like stes, buildings, storeys, and spaces (i.e. + rooms). You can't physically touch these spaces, but you can touch the + products contained within these spaces. - To state that a product is contained in a space, you will use a - "containment" relationship. Containment is a very common relationship - used to create the hierarchical spatial decomposition tree. For example, - you might say that "This wall is on the third building storey", or "this - table is in the living room space". + To state that a product is contained in a space, you will use a + "containment" relationship. Containment is a very common relationship + used to create the hierarchical spatial decomposition tree. For example, + you might say that "This wall is on the third building storey", or "this + table is in the living room space". - The distinguishing factor between aggregation and containment is that - aggregation occurs between objects of the same type (e.g. a large space - is made up of smaller spaces), whereas containment is between two - different types: explicitly saying that a physical product exists within - a virtual space. + The distinguishing factor between aggregation and containment is that + aggregation occurs between objects of the same type (e.g. a large space + is made up of smaller spaces), whereas containment is between two + different types: explicitly saying that a physical product exists within + a virtual space. - Containment is critical in construction management, to know which - objects are in which spaces, as often you would divide your construction - schedule into storey by storey, or zone by zone. Containment is also - critical in facility management, as it indicates through which space - equipment may be accessed for maintenance purposes. + Containment is critical in construction management, to know which + objects are in which spaces, as often you would divide your construction + schedule into storey by storey, or zone by zone. Containment is also + critical in facility management, as it indicates through which space + equipment may be accessed for maintenance purposes. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an aggregate relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an aggregate relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - :param products: A list of physical IfcElements existing in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :return: The IfcRelContainedInSpatialStructure relationship instance - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: A list of physical IfcElements existing in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: The IfcRelContainedInSpatialStructure relationship instance + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a wall and furniture - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # Create a wall and furniture + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # The wall is in the storey, and the furniture is in the space - ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) - ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space) - """ - self.file = file - self.settings = { - "products": products, - "relating_structure": relating_structure, - } + # The wall is in the storey, and the furniture is in the space + ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) + ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space) + """ + settings = { + "products": products, + "relating_structure": relating_structure, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - products = set(self.settings["products"]) - relating_structure = self.settings["relating_structure"] - structure_rel = next(iter(relating_structure.ContainsElements), None) + products = set(settings["products"]) + relating_structure = settings["relating_structure"] + structure_rel = next(iter(relating_structure.ContainsElements), None) - previous_containers_rels: set[ifcopenshell.entity_instance] = set() - products_without_containers: list[ifcopenshell.entity_instance] = [] - products_with_containers: list[ifcopenshell.entity_instance] = [] + previous_containers_rels: set[ifcopenshell.entity_instance] = set() + products_without_containers: list[ifcopenshell.entity_instance] = [] + products_with_containers: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for product in products: - product_rel = next(iter(product.ContainedInStructure), None) + # check if there is anything to change + for product in products: + product_rel = next(iter(product.ContainedInStructure), None) - if product_rel is None: - products_without_containers.append(product) - continue + if product_rel is None: + products_without_containers.append(product) + continue - # either structure_rel is None or product is part of different rel - if product_rel != structure_rel: - previous_containers_rels.add(product_rel) - products_with_containers.append(product) + # either structure_rel is None or product is part of different rel + if product_rel != structure_rel: + previous_containers_rels.add(product_rel) + products_with_containers.append(product) - # products with already assigned containers will be skipped + # products with already assigned containers will be skipped - products_to_change = products_without_containers + products_with_containers - # nothing to change - if not products_to_change: - return structure_rel + products_to_change = products_without_containers + products_with_containers + # nothing to change + if not products_to_change: + return structure_rel - # can be either only aggregated or only contained at the same time - ifcopenshell.api.run("aggregate.unassign_object", self.file, products=products_without_containers) + # can be either only aggregated or only contained at the same time + ifcopenshell.api.run("aggregate.unassign_object", file, products=products_without_containers) - # unassign elements from previous containers - for rel in previous_containers_rels: - related_elements = set(rel.RelatedElements) - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new container - if structure_rel: - structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": structure_rel}) + # unassign elements from previous containers + for rel in previous_containers_rels: + related_elements = set(rel.RelatedElements) - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - structure_rel = self.file.create_entity( - "IfcRelContainedInSpatialStructure", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedElements": list(products), - "RelatingStructure": self.settings["relating_structure"], - } + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new container + if structure_rel: + structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": structure_rel}) + else: + structure_rel = file.create_entity( + "IfcRelContainedInSpatialStructure", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedElements": list(products), + "RelatingStructure": settings["relating_structure"], + } + ) + + # localize placement relative to a new container for affected products + for product in products_to_change: + placement = getattr(product, "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=product, + matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), + is_si=False, ) - # localize placement relative to a new container for affected products - for product in products_to_change: - placement = getattr(product, "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=product, - matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), - is_si=False, - ) - - return structure_rel + return structure_rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index 6902018b46..50eb72305b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -21,70 +21,66 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Dereferences a list of products and space +def dereference_structure( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> None: + """Dereferences a list of products and space - :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :return: None - :rtype: None + :param products: The list of physical IfcElements that exists in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a column, this column spans 3 storeys - column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The column is contained in the lowermost storey - ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) - # And referenced in the others - ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2) - ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3) + # And referenced in the others + ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2) + ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3) - # Actually, it only goes up to storey 2. - ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3) - """ - self.file = file - self.settings = {"products": products, "relating_structure": relating_structure} + # Actually, it only goes up to storey 2. + ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3) + """ + settings = {"products": products, "relating_structure": relating_structure} - def execute(self) -> None: - products = set(self.settings["products"]) - for rel in self.settings["relating_structure"].ReferencesElements: - related_elements = set(rel.RelatedElements) - if not related_elements.intersection(products): - continue - related_elements = related_elements - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + products = set(settings["products"]) + for rel in settings["relating_structure"].ReferencesElements: + related_elements = set(rel.RelatedElements) + if not related_elements.intersection(products): + continue + related_elements = related_elements - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index 32ef580b96..48b5c6bc1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -22,103 +22,99 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Denote that a list products is related to a list of spatial structures +def reference_structure( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Denote that a list products is related to a list of spatial structures - This is similar to ifcopenshell.api.spatial.assign_container, except - that containment can only occur between a product and a single spatial - structure element. This is fine if a wall is on level 1, but not - appropriate if you have a multistorey column on multiple levels, or a - door with a to and from space, or a stair going from one floor to - another floor. This is where spatial referencing is used. + This is similar to ifcopenshell.api.spatial.assign_container, except + that containment can only occur between a product and a single spatial + structure element. This is fine if a wall is on level 1, but not + appropriate if you have a multistorey column on multiple levels, or a + door with a to and from space, or a stair going from one floor to + another floor. This is where spatial referencing is used. - Typically, the product will be contained in the lowermost, constructed - first, or primarily accessible space. For a multistorey column or stair, - the column or stair will therefore be contained in the lowermost storey. - Then, any other storeys will be referenced. + Typically, the product will be contained in the lowermost, constructed + first, or primarily accessible space. For a multistorey column or stair, + the column or stair will therefore be contained in the lowermost storey. + Then, any other storeys will be referenced. - Referencing is non-hierarchical, so a door may be referenced in multiple - spaces simultaneously. + Referencing is non-hierarchical, so a door may be referenced in multiple + spaces simultaneously. - :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :type relating_structure: ifcopenshell.entity_instance - :return: The IfcRelReferencedInSpatialStructure relationship instance - or `None` if `products` was an empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of physical IfcElements that exists in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :type relating_structure: ifcopenshell.entity_instance + :return: The IfcRelReferencedInSpatialStructure relationship instance + or `None` if `products` was an empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a column, this column spans 3 storeys - column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The column is contained in the lowermost storey - ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) - # And referenced in the others - ifcopenshell.api.run( - "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3] - ) - """ - self.file = file - self.settings = { - "products": products, - "relating_structure": relating_structure, - } + # And referenced in the others + ifcopenshell.api.run( + "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3] + ) + """ + settings = { + "products": products, + "relating_structure": relating_structure, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - structure = self.settings["relating_structure"] - products = set(self.settings["products"]) + structure = settings["relating_structure"] + products = set(settings["products"]) - if not products: - return + if not products: + return - referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure) - products_to_assign = products - referenced - rel = next(iter(structure.ReferencesElements), None) - - if not products_to_assign: - return rel - - if rel is None: - rel = self.file.create_entity( - "IfcRelReferencedInSpatialStructure", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedElements": list(products_to_assign), - "RelatingStructure": structure, - } - ) - else: - related_elements = set(rel.RelatedElements) | products_to_assign - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure) + products_to_assign = products - referenced + rel = next(iter(structure.ReferencesElements), None) + if not products_to_assign: return rel + + if rel is None: + rel = file.create_entity( + "IfcRelReferencedInSpatialStructure", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedElements": list(products_to_assign), + "RelatingStructure": structure, + } + ) + else: + related_elements = set(rel.RelatedElements) | products_to_assign + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py index d1418d3be5..b6afbc13c9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py @@ -21,56 +21,53 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Unassigns a container from products. +def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Unassigns a container from products. - :param product: A list of IfcProducts to remove the containment from. - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param product: A list of IfcProducts to remove the containment from. + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The wall is in the storey - ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) + # The wall is in the storey + ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) - # Not anymore! - ifcopenshell.api.run("spatial.unassign_container", model, products=[wall]) - """ - self.file = file - self.settings = { - "products": products, - } + # Not anymore! + ifcopenshell.api.run("spatial.unassign_container", model, products=[wall]) + """ + settings = { + "products": products, + } - def execute(self) -> None: - products = set(self.settings["products"]) - rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None))) + products = set(settings["products"]) + rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None))) - for rel in rels: - related_elements = set(rel.RelatedElements) - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_elements = set(rel.RelatedElements) - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py index e0caddbe3c..3bdaf8c004 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py @@ -15,3 +15,25 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_structural_activity import add_structural_activity +from .add_structural_analysis_model import add_structural_analysis_model +from .add_structural_boundary_condition import add_structural_boundary_condition +from .add_structural_load import add_structural_load +from .add_structural_load_case import add_structural_load_case +from .add_structural_load_group import add_structural_load_group +from .add_structural_member_connection import add_structural_member_connection +from .assign_structural_analysis_model import assign_structural_analysis_model +from .edit_structural_analysis_model import edit_structural_analysis_model +from .edit_structural_boundary_condition import edit_structural_boundary_condition +from .edit_structural_connection_cs import edit_structural_connection_cs +from .edit_structural_item_axis import edit_structural_item_axis +from .edit_structural_load import edit_structural_load +from .edit_structural_load_case import edit_structural_load_case +from .remove_structural_analysis_model import remove_structural_analysis_model +from .remove_structural_boundary_condition import remove_structural_boundary_condition +from .remove_structural_connection_condition import remove_structural_connection_condition +from .remove_structural_load import remove_structural_load +from .remove_structural_load_case import remove_structural_load_case +from .remove_structural_load_group import remove_structural_load_group +from .unassign_structural_analysis_model import unassign_structural_analysis_model diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py index faf1daf366..4be210fcf1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py @@ -19,65 +19,61 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, +def add_structural_activity( + file, + ifc_class="IfcStructuralPlanarAction", + predefined_type="CONST", + global_or_local="GLOBAL_COORDS", + applied_load=None, + structural_member=None, +) -> None: + """Adds a new structural activity + + A structural activity is either a structural action or a reaction. It + may be applied to a point, a curve, or a planar surface, and may be a + constant load, linear, etc. + + The activity must be defined using an applied load, and associated with + a structural member. + + :param ifc_class: Choose from any subtype of IfcStructuralActivity. + :type ifc_class: str + :param predefined_type: View the IFC documentation for what valid + predefined types may be chosen. + :type predefined_type: str + :param global_or_local: The location coordinates of the load is always + defined locally relative to the structural member the activity is + assigned to. However, the directions of the applied load may either + be specified globally or locally depending on how this argument is + set. Choose from GLOBAL_COORDS or LOCAL_COORDS. + :type global_or_local: str + :param applied_load: The IfcStructuralLoad that is applied in this + activity. + :type applied_load: ifcopenshell.entity_instance + :param structural_member: The IfcStructuralMember that the load is + applied to. + :type structural_member: ifcopenshell.entity_instance + :return: The newly created entity based on the ifc_class + :rtype: ifcopenshell.entity_instance + """ + settings = { + "ifc_class": ifc_class, + "predefined_type": predefined_type, + "global_or_local": global_or_local, + "applied_load": applied_load, + "structural_member": structural_member, + } + + activity = ifcopenshell.api.run( + "root.create_entity", file, - ifc_class="IfcStructuralPlanarAction", - predefined_type="CONST", - global_or_local="GLOBAL_COORDS", - applied_load=None, - structural_member=None, - ): - """Adds a new structural activity + ifc_class=settings["ifc_class"], + predefined_type=settings["predefined_type"], + ) + activity.AppliedLoad = settings["applied_load"] + activity.GlobalOrLocal = settings["global_or_local"] - A structural activity is either a structural action or a reaction. It - may be applied to a point, a curve, or a planar surface, and may be a - constant load, linear, etc. - - The activity must be defined using an applied load, and associated with - a structural member. - - :param ifc_class: Choose from any subtype of IfcStructuralActivity. - :type ifc_class: str - :param predefined_type: View the IFC documentation for what valid - predefined types may be chosen. - :type predefined_type: str - :param global_or_local: The location coordinates of the load is always - defined locally relative to the structural member the activity is - assigned to. However, the directions of the applied load may either - be specified globally or locally depending on how this argument is - set. Choose from GLOBAL_COORDS or LOCAL_COORDS. - :type global_or_local: str - :param applied_load: The IfcStructuralLoad that is applied in this - activity. - :type applied_load: ifcopenshell.entity_instance - :param structural_member: The IfcStructuralMember that the load is - applied to. - :type structural_member: ifcopenshell.entity_instance - :return: The newly created entity based on the ifc_class - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "ifc_class": ifc_class, - "predefined_type": predefined_type, - "global_or_local": global_or_local, - "applied_load": applied_load, - "structural_member": structural_member, - } - - def execute(self): - activity = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class=self.settings["ifc_class"], - predefined_type=self.settings["predefined_type"], - ) - activity.AppliedLoad = self.settings["applied_load"] - activity.GlobalOrLocal = self.settings["global_or_local"] - - rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralActivity") - rel.RelatingElement = self.settings["structural_member"] - rel.RelatedStructuralActivity = activity - return activity + rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralActivity") + rel.RelatingElement = settings["structural_member"] + rel.RelatedStructuralActivity = activity + return activity diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py index 39181f9a4c..837fd29cce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py @@ -20,30 +20,27 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file): - """Add a new structural analysis model +def add_structural_analysis_model(file) -> None: + """Add a new structural analysis model - A structural analysis model is a group of all the loads, reactions, - structural members, and structural connections required to describe a - structural analysis model. + A structural analysis model is a group of all the loads, reactions, + structural members, and structural connections required to describe a + structural analysis model. - A 3D analytical model is assumed. + A 3D analytical model is assumed. - :return: The newly created IfcStructuralAnalysisModel - :rtype: ifcopenshell.entity_instance + :return: The newly created IfcStructuralAnalysisModel + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a fresh blank structural analysis - analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model) - """ - self.file = file - self.settings = {} + # Create a fresh blank structural analysis + analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model) + """ + settings = {} - def execute(self): - return ifcopenshell.api.run( - "root.create_entity", self.file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D" - ) + return ifcopenshell.api.run( + "root.create_entity", file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D" + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py index 1cd9dfef9f..5aef16efed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py @@ -17,55 +17,50 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition"): - """Adds a new structural boundary condition to a structural connection +def add_structural_boundary_condition(file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition") -> None: + """Adds a new structural boundary condition to a structural connection - The type of boundary condition depends on the connection. Point - connections will have a node condition, curve connections will have an - edge condition, and surface connections will have a face condition. + The type of boundary condition depends on the connection. Point + connections will have a node condition, curve connections will have an + edge condition, and surface connections will have a face condition. - :param name: The name of the boundary condition. - :type name: str,optional - :param connection: The IfcStructuralConnection to apply the boundary - condition to. This will determine the type of condition that is - created. If no connection is supplied, an orphan boundary condition - will be created using the ifc_class that you specify. - :type connection: ifcopenshell.entity_instance,optional - :param ifc_class: The class of IfcBoundaryCondition to create, only - relevant if you do not specify a connection and want to create an - orphaned boundary condition. - :type ifc_class: str,optional - :return: The newly created IfcBoundaryCondition - :rtype: ifcopenshell.entity_instance + :param name: The name of the boundary condition. + :type name: str,optional + :param connection: The IfcStructuralConnection to apply the boundary + condition to. This will determine the type of condition that is + created. If no connection is supplied, an orphan boundary condition + will be created using the ifc_class that you specify. + :type connection: ifcopenshell.entity_instance,optional + :param ifc_class: The class of IfcBoundaryCondition to create, only + relevant if you do not specify a connection and want to create an + orphaned boundary condition. + :type ifc_class: str,optional + :return: The newly created IfcBoundaryCondition + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection) - """ - self.file = file - self.settings = {"name": name, "connection": connection, "ifc_class": ifc_class} + ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection) + """ + settings = {"name": name, "connection": connection, "ifc_class": ifc_class} - def execute(self): - if self.settings["connection"]: - # assign boundary condition to a connection - if self.settings["connection"].is_a("IfcRelConnectsStructuralMember"): - related_connection = self.settings["connection"].RelatedStructuralConnection - else: - related_connection = self.settings["connection"] - - if related_connection.is_a("IfcStructuralPointConnection"): - boundary_class = "IfcBoundaryNodeCondition" - elif related_connection.is_a("IfcStructuralCurveConnection"): - boundary_class = "IfcBoundaryEdgeCondition" - elif related_connection.is_a("IfcStructuralSurfaceConnection"): - boundary_class = "IfcBoundaryFaceCondition" - - self.settings["connection"].AppliedCondition = self.file.create_entity( - boundary_class, Name=self.settings["name"] - ) + if settings["connection"]: + # assign boundary condition to a connection + if settings["connection"].is_a("IfcRelConnectsStructuralMember"): + related_connection = settings["connection"].RelatedStructuralConnection else: - # add an orphan boundary condition - return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"]) + related_connection = settings["connection"] + + if related_connection.is_a("IfcStructuralPointConnection"): + boundary_class = "IfcBoundaryNodeCondition" + elif related_connection.is_a("IfcStructuralCurveConnection"): + boundary_class = "IfcBoundaryEdgeCondition" + elif related_connection.is_a("IfcStructuralSurfaceConnection"): + boundary_class = "IfcBoundaryFaceCondition" + + settings["connection"].AppliedCondition = file.create_entity(boundary_class, Name=settings["name"]) + else: + # add an orphan boundary condition + return file.create_entity(settings["ifc_class"], Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py index 6d51d7dc22..3cb06cd513 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py @@ -19,35 +19,32 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, name=None, ifc_class="IfcStructuralLoadLinearForce"): - """Adds a new structural load +def add_structural_load(file, name=None, ifc_class="IfcStructuralLoadLinearForce") -> None: + """Adds a new structural load - Structural loads may be actions or reactions. A simple load might be a - static and be linear, planar, or a single point. Alternatively, loads - may be defined as a configuration of multiple loads. + Structural loads may be actions or reactions. A simple load might be a + static and be linear, planar, or a single point. Alternatively, loads + may be defined as a configuration of multiple loads. - :param name: The name of the load - :type name: str,optional - :param ifc_class: The subtype of IfcStructuralLoad to create. Consult - the IFC documentation to see all the types of loads. - :type ifc_class: str - :return: The newly created load entity, depending on the ifc_class - specified. - :rtype: ifcopenshell.entity_instance + :param name: The name of the load + :type name: str,optional + :param ifc_class: The subtype of IfcStructuralLoad to create. Consult + the IFC documentation to see all the types of loads. + :type ifc_class: str + :return: The newly created load entity, depending on the ifc_class + specified. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple linear load - ifcopenshell.api.run("structural.add_structural_load", model) - """ - self.file = file - self.settings = { - "name": name, - "ifc_class": ifc_class, - } + # Create a simple linear load + ifcopenshell.api.run("structural.add_structural_load", model) + """ + settings = { + "name": name, + "ifc_class": ifc_class, + } - def execute(self): - return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"]) + return file.create_entity(settings["ifc_class"], Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py index afc4e676db..e3d2c4f6c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py @@ -19,39 +19,34 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED" - ): - """Adds a new load case, which is a collection of related load groups +def add_structural_load_case(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None: + """Adds a new load case, which is a collection of related load groups - :param name: The name of the load case - :type name: str - :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, - or VARIABLE_Q, taken from the Eurocode standard. - :type action_type: str - :param action_source: The source of the load case, such as DEAD_LOAD_G, - LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult - IfcActionSourceTypeEnum in the IFC documentation. - :type action_source: str - :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "name": name, - "action_type": action_type, - "action_source": action_source, - } + :param name: The name of the load case + :type name: str + :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, + or VARIABLE_Q, taken from the Eurocode standard. + :type action_type: str + :param action_source: The source of the load case, such as DEAD_LOAD_G, + LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult + IfcActionSourceTypeEnum in the IFC documentation. + :type action_source: str + :return: The new IfcStructuralLoadCase + :rtype: ifcopenshell.entity_instance + """ + settings = { + "name": name, + "action_type": action_type, + "action_source": action_source, + } - def execute(self): - load_case = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcStructuralLoadCase", - predefined_type="LOAD_CASE", - name=self.settings["name"], - ) - load_case.ActionType = self.settings["action_type"] - load_case.ActionSource = self.settings["action_source"] - return load_case + load_case = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcStructuralLoadCase", + predefined_type="LOAD_CASE", + name=settings["name"], + ) + load_case.ActionType = settings["action_type"] + load_case.ActionSource = settings["action_source"] + return load_case diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py index 497977fe6e..3f450df5c8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py @@ -19,39 +19,34 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED" - ): - """Adds a new load group, which is a collection of related loads +def add_structural_load_group(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None: + """Adds a new load group, which is a collection of related loads - :param name: The name of the load group - :type name: str - :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, - or VARIABLE_Q, taken from the Eurocode standard. - :type action_type: str - :param action_source: The source of the load case, such as DEAD_LOAD_G, - LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult - IfcActionSourceTypeEnum in the IFC documentation. - :type action_source: str - :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "name": name, - "action_type": action_type, - "action_source": action_source, - } + :param name: The name of the load group + :type name: str + :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, + or VARIABLE_Q, taken from the Eurocode standard. + :type action_type: str + :param action_source: The source of the load case, such as DEAD_LOAD_G, + LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult + IfcActionSourceTypeEnum in the IFC documentation. + :type action_source: str + :return: The new IfcStructuralLoadCase + :rtype: ifcopenshell.entity_instance + """ + settings = { + "name": name, + "action_type": action_type, + "action_source": action_source, + } - def execute(self): - load_group = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcStructuralLoadGroup", - predefined_type="LOAD_GROUP", - name=self.settings["name"], - ) - load_group.ActionType = self.settings["action_type"] - load_group.ActionSource = self.settings["action_source"] - return load_group + load_group = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcStructuralLoadGroup", + predefined_type="LOAD_GROUP", + name=settings["name"], + ) + load_group.ActionType = settings["action_type"] + load_group.ActionSource = settings["action_source"] + return load_group diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py index eda5fc96c2..792c03b66a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py @@ -20,30 +20,27 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_structural_member=None, related_structural_connection=None): - """Relates a structural member and a structural connection +def add_structural_member_connection(file, relating_structural_member=None, related_structural_connection=None) -> None: + """Relates a structural member and a structural connection - :param relating_structural_member: The IfcStructuralMember to have a - connection added to it. - :type relating_structural_member: ifcopenshell.entity_instance - :param related_structural_connection: The IfcStructuralConnection to add - to the IfcStructuralMember. - :type related_structural_connection: ifcopenshell.entity_instance - :return: The IfcRelConnectsStructuralMember relationship - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "relating_structural_member": relating_structural_member, - "related_structural_connection": related_structural_connection, - } + :param relating_structural_member: The IfcStructuralMember to have a + connection added to it. + :type relating_structural_member: ifcopenshell.entity_instance + :param related_structural_connection: The IfcStructuralConnection to add + to the IfcStructuralMember. + :type related_structural_connection: ifcopenshell.entity_instance + :return: The IfcRelConnectsStructuralMember relationship + :rtype: ifcopenshell.entity_instance + """ + settings = { + "relating_structural_member": relating_structural_member, + "related_structural_connection": related_structural_connection, + } - def execute(self): - for connection in self.settings["related_structural_connection"].ConnectsStructuralMembers or []: - if connection.RelatingStructuralMember == self.settings["relating_structural_member"]: - return - rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralMember") - rel.RelatingStructuralMember = self.settings["relating_structural_member"] - rel.RelatedStructuralConnection = self.settings["related_structural_connection"] - return rel + for connection in settings["related_structural_connection"].ConnectsStructuralMembers or []: + if connection.RelatingStructuralMember == settings["relating_structural_member"]: + return + rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralMember") + rel.RelatingStructuralMember = settings["relating_structural_member"] + rel.RelatedStructuralConnection = settings["related_structural_connection"] + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py index 61f771c982..19c31e34ff 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py @@ -20,37 +20,34 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, product=None, structural_analysis_model=None): - """Assigns a load or structural member to an analysis model +def assign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None: + """Assigns a load or structural member to an analysis model - :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance - :param structural_analysis_model: The IfcStructuralAnalysisModel that - the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "product": product, - "structural_analysis_model": structural_analysis_model, - } + :param product: The structural element that is part of the analysis. + :type product: ifcopenshell.entity_instance + :param structural_analysis_model: The IfcStructuralAnalysisModel that + the structural element is related to. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + :rtype: ifcopenshell.entity_instance + """ + settings = { + "product": product, + "structural_analysis_model": structural_analysis_model, + } - def execute(self): - if not self.settings["structural_analysis_model"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], - "RelatingGroup": self.settings["structural_analysis_model"], - } - ) - rel = self.settings["structural_analysis_model"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - related_objects.add(self.settings["product"]) - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + if not settings["structural_analysis_model"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["product"]], + "RelatingGroup": settings["structural_analysis_model"], + } + ) + rel = settings["structural_analysis_model"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + related_objects.add(settings["product"]) + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py index 39c46f6fe7..7c41c59478 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py @@ -17,24 +17,21 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_analysis_model=None, attributes=None): - """Edits the attributes of an IfcStructuralAnalysisModel +def edit_structural_analysis_model(file, structural_analysis_model=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralAnalysisModel - For more information about the attributes and data types of an - IfcStructuralAnalysisModel, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralAnalysisModel, consult the IFC documentation. - :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit - :type structural_analysis_model: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}} + :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit + :type structural_analysis_model: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["structural_analysis_model"], name, value) - return self.settings["structural_analysis_model"] + for name, value in settings["attributes"].items(): + setattr(settings["structural_analysis_model"], name, value) + return settings["structural_analysis_model"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py index e6814c5242..2674a4869e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py @@ -17,29 +17,26 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, condition=None, attributes=None): - """Edits the attributes of an IfcBoundaryCondition +def edit_structural_boundary_condition(file, condition=None, attributes=None) -> None: + """Edits the attributes of an IfcBoundaryCondition - For more information about the attributes and data types of an - IfcBoundaryCondition, consult the IFC documentation. + For more information about the attributes and data types of an + IfcBoundaryCondition, consult the IFC documentation. - :param condition: The IfcBoundaryCondition entity you want to edit - :type condition: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"condition": condition, "attributes": attributes or {}} + :param condition: The IfcBoundaryCondition entity you want to edit + :type condition: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"condition": condition, "attributes": attributes or {}} - def execute(self): - for name, data in self.settings["attributes"].items(): - if data["type"] == "string" or data["type"] == "null": - value = data["value"] - elif data["type"] == "IfcBoolean": - value = self.file.createIfcBoolean(data["value"]) - else: - value = self.file.create_entity(data["type"], data["value"]) - setattr(self.settings["condition"], name, value) + for name, data in settings["attributes"].items(): + if data["type"] == "string" or data["type"] == "null": + value = data["value"] + elif data["type"] == "IfcBoolean": + value = file.createIfcBoolean(data["value"]) + else: + value = file.create_entity(data["type"], data["value"]) + setattr(settings["condition"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py index a66bbb989e..89faa62ecd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_item=None, axis=None, ref_direction=None): - """Edits the coordinate system of a structural connection +def edit_structural_connection_cs(file, structural_item=None, axis=None, ref_direction=None) -> None: + """Edits the coordinate system of a structural connection - :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance - :param axis: The unit Z axis vector defined as a list of 3 floats. - Defaults to [0., 0., 1.]. - :type axis: list[float] - :param ref_direction: The unit X axis vector defined as a list of 3 - floats. Defaults to [1., 0., 0.]. - :type ref_direction: list[float] - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "structural_item": structural_item, - "axis": axis or [0.0, 0.0, 1.0], - "ref_direction": ref_direction or [1.0, 0.0, 0.0], - } + :param structural_item: The IfcStructuralItem you want to modify. + :type structural_item: ifcopenshell.entity_instance + :param axis: The unit Z axis vector defined as a list of 3 floats. + Defaults to [0., 0., 1.]. + :type axis: list[float] + :param ref_direction: The unit X axis vector defined as a list of 3 + floats. Defaults to [1., 0., 0.]. + :type ref_direction: list[float] + :return: None + :rtype: None + """ + settings = { + "structural_item": structural_item, + "axis": axis or [0.0, 0.0, 1.0], + "ref_direction": ref_direction or [1.0, 0.0, 0.0], + } - def execute(self): - if self.settings["structural_item"].ConditionCoordinateSystem is None: - point = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)) - ccs = self.file.createIfcAxis2Placement3D(point, None, None) - self.settings["structural_item"].ConditionCoordinateSystem = ccs + if settings["structural_item"].ConditionCoordinateSystem is None: + point = file.createIfcCartesianPoint((0.0, 0.0, 0.0)) + ccs = file.createIfcAxis2Placement3D(point, None, None) + settings["structural_item"].ConditionCoordinateSystem = ccs - ccs = self.settings["structural_item"].ConditionCoordinateSystem - if ccs.Axis and len(self.file.get_inverse(ccs.Axis)) == 1: - self.file.remove(ccs.Axis) - ccs.Axis = self.file.createIfcDirection(self.settings["axis"]) - if ccs.RefDirection and len(self.file.get_inverse(ccs.RefDirection)) == 1: - self.file.remove(ccs.RefDirection) - ccs.RefDirection = self.file.createIfcDirection(self.settings["ref_direction"]) + ccs = settings["structural_item"].ConditionCoordinateSystem + if ccs.Axis and len(file.get_inverse(ccs.Axis)) == 1: + file.remove(ccs.Axis) + ccs.Axis = file.createIfcDirection(settings["axis"]) + if ccs.RefDirection and len(file.get_inverse(ccs.RefDirection)) == 1: + file.remove(ccs.RefDirection) + ccs.RefDirection = file.createIfcDirection(settings["ref_direction"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py index ec4b163aca..dbb2541371 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py @@ -17,22 +17,19 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_item=None, axis=None): - """Edits the coordinate system of a structural connection +def edit_structural_item_axis(file, structural_item=None, axis=None) -> None: + """Edits the coordinate system of a structural connection - :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance - :param axis: The unit Z axis vector defined as a list of 3 floats. - Defaults to [0., 0., 1.]. - :type axis: list[float] - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]} + :param structural_item: The IfcStructuralItem you want to modify. + :type structural_item: ifcopenshell.entity_instance + :param axis: The unit Z axis vector defined as a list of 3 floats. + Defaults to [0., 0., 1.]. + :type axis: list[float] + :return: None + :rtype: None + """ + settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]} - def execute(self): - if len(self.file.get_inverse(self.settings["structural_item"].Axis)) == 1: - self.file.remove(self.settings["structural_item"].Axis) - self.settings["structural_item"].Axis = self.file.createIfcDirection(self.settings["axis"]) + if len(file.get_inverse(settings["structural_item"].Axis)) == 1: + file.remove(settings["structural_item"].Axis) + settings["structural_item"].Axis = file.createIfcDirection(settings["axis"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py index 3adba0ade9..2c577deb83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_load=None, attributes=None): - """Edits the attributes of an IfcStructuralLoad +def edit_structural_load(file, structural_load=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralLoad - For more information about the attributes and data types of an - IfcStructuralLoad, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralLoad, consult the IFC documentation. - :param structural_load: The IfcStructuralLoad entity you want to edit - :type structural_load: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_load": structural_load, "attributes": attributes or {}} + :param structural_load: The IfcStructuralLoad entity you want to edit + :type structural_load: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"structural_load": structural_load, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["structural_load"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["structural_load"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py index cffce454bf..4c84573795 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, load_case=None, attributes=None): - """Edits the attributes of an IfcStructuralLoadCase +def edit_structural_load_case(file, load_case=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralLoadCase - For more information about the attributes and data types of an - IfcStructuralLoadCase, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralLoadCase, consult the IFC documentation. - :param load_case: The IfcStructuralLoadCase entity you want to edit - :type load_case: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_case": load_case, "attributes": attributes or {}} + :param load_case: The IfcStructuralLoadCase entity you want to edit + :type load_case: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"load_case": load_case, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["load_case"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["load_case"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py index 4ebff6cc3d..b238562b18 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py @@ -20,28 +20,25 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, structural_analysis_model=None): - """Removes an analysis model +def remove_structural_analysis_model(file, structural_analysis_model=None) -> None: + """Removes an analysis model - Note that the contents of an analysis model are currently preserved. + Note that the contents of an analysis model are currently preserved. - :param structural_analysis_model: The IfcStructuralAnalysisModel to - remove. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_analysis_model": structural_analysis_model} + :param structural_analysis_model: The IfcStructuralAnalysisModel to + remove. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"structural_analysis_model": structural_analysis_model} - def execute(self): - for rel in self.settings["structural_analysis_model"].IsGroupedBy or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["structural_analysis_model"].OwnerHistory - self.file.remove(self.settings["structural_analysis_model"]) + for rel in settings["structural_analysis_model"].IsGroupedBy or []: + history = rel.OwnerHistory + file.remove(rel) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["structural_analysis_model"].OwnerHistory + file.remove(settings["structural_analysis_model"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py index 02cfb79e3c..7aa4f6bd74 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, connection=None, boundary_condition=None): - """Removes a condition from a connection, or an orphased boundary condition +def remove_structural_boundary_condition(file, connection=None, boundary_condition=None) -> None: + """Removes a condition from a connection, or an orphased boundary condition - :param connection: The IfcStructuralConnection to remove the condition - from. If omitted, it is assumed to be an orphaned condition. - :type connection: ifcopenshell.entity_instance,optional - :param boundary_condition: The IfcBoundaryCondition to remove. - :type boundary_condition: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"connection": connection, "boundary_condition": boundary_condition} + :param connection: The IfcStructuralConnection to remove the condition + from. If omitted, it is assumed to be an orphaned condition. + :type connection: ifcopenshell.entity_instance,optional + :param boundary_condition: The IfcBoundaryCondition to remove. + :type boundary_condition: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"connection": connection, "boundary_condition": boundary_condition} - def execute(self): - if self.settings["connection"]: - # remove boundary condition from a connection - if not self.settings["connection"].AppliedCondition: - return - if len(self.file.get_inverse(self.settings["connection"].AppliedCondition)) == 1: - self.file.remove(self.settings["connection"].AppliedCondition) - self.settings["connection"].AppliedCondition = None - else: - # remove the boundary condition - for conn in self.file.get_inverse(self.settings["boundary_condition"]): - conn.AppliedCondition = None - self.file.remove(self.settings["boundary_condition"]) + if settings["connection"]: + # remove boundary condition from a connection + if not settings["connection"].AppliedCondition: + return + if len(file.get_inverse(settings["connection"].AppliedCondition)) == 1: + file.remove(settings["connection"].AppliedCondition) + settings["connection"].AppliedCondition = None + else: + # remove the boundary condition + for conn in file.get_inverse(settings["boundary_condition"]): + conn.AppliedCondition = None + file.remove(settings["boundary_condition"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py index 28ce9fc4c9..21ed51f712 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py @@ -21,28 +21,25 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relation=None): - """Removes a relationship between a connection and a condition +def remove_structural_connection_condition(file, relation=None) -> None: + """Removes a relationship between a connection and a condition - The condition and the member itself is preserved. + The condition and the member itself is preserved. - :param relation: The IfcRelConnectsStructuralMember to remove. - :type relation: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"relation": relation} + :param relation: The IfcRelConnectsStructuralMember to remove. + :type relation: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"relation": relation} - def execute(self): - if self.settings["relation"].AppliedCondition: - ifcopenshell.api.run( - "structural.remove_structural_boundary_condition", - self.file, - connection=self.settings["relation"].RelatedStructuralConnection - ) - history = self.settings["relation"].OwnerHistory - self.file.remove(self.settings["relation"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if settings["relation"].AppliedCondition: + ifcopenshell.api.run( + "structural.remove_structural_boundary_condition", + file, + connection=settings["relation"].RelatedStructuralConnection, + ) + history = settings["relation"].OwnerHistory + file.remove(settings["relation"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py index 55b83a7f1b..afe97029ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py @@ -17,17 +17,14 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_load=None): - """Removes a structural load +def remove_structural_load(file, structural_load=None) -> None: + """Removes a structural load - :param structural_load: The IfcStructuralLoad to remove. - :type structural_load: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_load": structural_load} + :param structural_load: The IfcStructuralLoad to remove. + :type structural_load: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"structural_load": structural_load} - def execute(self): - self.file.remove(self.settings["structural_load"]) + file.remove(settings["structural_load"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py index e331309239..de317ed354 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py @@ -21,25 +21,22 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, load_case=None): - """Removes a structural load case +def remove_structural_load_case(file, load_case=None) -> None: + """Removes a structural load case - :param load_case: The IfcStructuralLoadCase to remove. - :type load_case: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_case": load_case} + :param load_case: The IfcStructuralLoadCase to remove. + :type load_case: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"load_case": load_case} - def execute(self): - # TODO: do a deep purge - for rel in self.settings["load_case"].IsGroupedBy or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["load_case"].OwnerHistory - self.file.remove(self.settings["load_case"]) - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for rel in settings["load_case"].IsGroupedBy or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["load_case"].OwnerHistory + file.remove(settings["load_case"]) + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py index 93500aba1b..541dd87811 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py @@ -21,27 +21,24 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, load_group=None): - """Removes a structural load group +def remove_structural_load_group(file, load_group=None) -> None: + """Removes a structural load group - :param load_group: The IfcStructuralLoadGroup to remove. - :type load_group: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_group": load_group} + :param load_group: The IfcStructuralLoadGroup to remove. + :type load_group: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"load_group": load_group} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["load_group"]): - if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["load_group"].OwnerHistory - self.file.remove(self.settings["load_group"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for inverse in file.get_inverse(settings["load_group"]): + if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["load_group"].OwnerHistory + file.remove(settings["load_group"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py index 5a86a6a9f3..b4dedc2832 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py @@ -21,35 +21,32 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, product=None, structural_analysis_model=None): - """Removes a relationship between a structural element and the analysis model +def unassign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None: + """Removes a relationship between a structural element and the analysis model - :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance - :param structural_analysis_model: The IfcStructuralAnalysisModel that - the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "product": product, - "structural_analysis_model": structural_analysis_model, - } + :param product: The structural element that is part of the analysis. + :type product: ifcopenshell.entity_instance + :param structural_analysis_model: The IfcStructuralAnalysisModel that + the structural element is related to. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = { + "product": product, + "structural_analysis_model": structural_analysis_model, + } - def execute(self): - if not self.settings["structural_analysis_model"].IsGroupedBy: - return - rel = self.settings["structural_analysis_model"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - related_objects.remove(self.settings["product"]) - if len(related_objects): - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if not settings["structural_analysis_model"].IsGroupedBy: + return + rel = settings["structural_analysis_model"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + related_objects.remove(settings["product"]) + if len(related_objects): + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py index e0caddbe3c..df9fd47518 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_style import add_style +from .add_surface_style import add_surface_style +from .add_surface_textures import add_surface_textures +from .assign_material_style import assign_material_style +from .assign_representation_styles import assign_representation_styles +from .edit_presentation_style import edit_presentation_style +from .edit_surface_style import edit_surface_style +from .remove_style import remove_style +from .remove_styled_representation import remove_styled_representation +from .remove_surface_style import remove_surface_style +from .unassign_material_style import unassign_material_style +from .unassign_representation_styles import unassign_representation_styles diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py index 650043039f..599feaef3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, ifc_class="IfcSurfaceStyle"): - """Add a new presentation style +def add_style(file, name=None, ifc_class="IfcSurfaceStyle") -> None: + """Add a new presentation style - A presentation style is a container of visual settings (called - presentation items) that affect the appearance of objects. There are - four types of style: + A presentation style is a container of visual settings (called + presentation items) that affect the appearance of objects. There are + four types of style: - - Surface styles, which give 3D objects (which have surfaces / faces) - their colours and textures. This is the most common type of style. - - Curve styles, which give 2D and 3D curves, lines, polylines, their - stroke thickness and colour. - - Fill area styles, which gives 2D polygons and flat 3D planes their - colours, hatch patterns, tiled patterns, and pattern scales. - - Text styles, which gives text their font family, weight, variant, - size, indentation, alignment, decoration, spacing, and transformation. + - Surface styles, which give 3D objects (which have surfaces / faces) + their colours and textures. This is the most common type of style. + - Curve styles, which give 2D and 3D curves, lines, polylines, their + stroke thickness and colour. + - Fill area styles, which gives 2D polygons and flat 3D planes their + colours, hatch patterns, tiled patterns, and pattern scales. + - Text styles, which gives text their font family, weight, variant, + size, indentation, alignment, decoration, spacing, and transformation. - Once you have created a presentation style object, you can further - define the properties of your style using other API functions by adding - presentation items, such as ifcopenshell.api.style.add_surface_style. + Once you have created a presentation style object, you can further + define the properties of your style using other API functions by adding + presentation items, such as ifcopenshell.api.style.add_surface_style. - :param name: The name of the style. Used to easily identify it using a - style library. - :type name: str,optional - :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle, - IfcFillAreaStyle, or IfcTextStyle. - :type ifc_class: str - :return: The newly created style element, based on the provided - ifc_class. - :rtype: ifcopenshell.entity_instance + :param name: The name of the style. Used to easily identify it using a + style library. + :type name: str,optional + :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle, + IfcFillAreaStyle, or IfcTextStyle. + :type ifc_class: str + :return: The newly created style element, based on the provided + ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - """ - self.file = file - self.settings = {"name": name, "ifc_class": ifc_class} + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + """ + settings = {"name": name, "ifc_class": ifc_class} - def execute(self): - if self.settings["ifc_class"] == "IfcSurfaceStyle": - # Name is filled out because Revit treats this incorrectly as the material name - return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH") + if settings["ifc_class"] == "IfcSurfaceStyle": + # Name is filled out because Revit treats this incorrectly as the material name + return file.createIfcSurfaceStyle(settings["name"], "BOTH") diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py index 32064811f9..c8f9c415d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py @@ -20,117 +20,112 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None): - """Adds a new presentation item to a surface style +def add_surface_style(file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None) -> None: + """Adds a new presentation item to a surface style - A surface style can have multiple different types of presentation items - assigned to it: + A surface style can have multiple different types of presentation items + assigned to it: - - Shading, this is the simplest item, which defines a single basic - colour and transparency that can be used to display the object on a - screen. It is an indicative colour of what the object would be in real - life. It is commonly incorrectly abused to colour code systems for MEP - equipment or object types for structural steel. If you just want to - give something a colour, this is what you need. - - Rendering, this is an advanced extension of shading, which includes - the definition of a shader for a rendering engine. You may select the - reflectance / lighting model such as PHYSICAL, for PBR style - rendering, or FLAT, for flat shading, or PHONG for older biased - rendering workflows. Based on the chosen lighting model, you may then - specify the appropriate colour maps, such as diffuse colours, - specularity, emissive component, etc. These lighting models are fully - compatible with glTF and X3D. This should be used if your model is - prepared to be rendered by a rendering engine which is compatible with - glTF / X3D shader descriptions. If you are doing archviz or 3D - rendering, this is what you need. - - Textures, this is a special type of Rendering presentation item that - uses image textures instead of single colours. Textures may be either - mapped using a bounding box stretch mapping, or with UV coordinates - for mesh-like geometry. - - Lighting, this is used to define photometrically accurate colour - parameters used in lighting simulation. If you are a simulationist, - this is what you need. - - Reflectance, this is a special type of Lighting presentation item - which includes some lesser used photometric properties, typically - required for advanced materials like glazing. - - External, this is for any other surface style defined using an - external URI. This is relevant if you are using a third-party non-glTF - compatible shader definition such as for Cycles, Renderman, V-Ray, - etc, or a complex lighting simulation definition, such as for - Radiance. + - Shading, this is the simplest item, which defines a single basic + colour and transparency that can be used to display the object on a + screen. It is an indicative colour of what the object would be in real + life. It is commonly incorrectly abused to colour code systems for MEP + equipment or object types for structural steel. If you just want to + give something a colour, this is what you need. + - Rendering, this is an advanced extension of shading, which includes + the definition of a shader for a rendering engine. You may select the + reflectance / lighting model such as PHYSICAL, for PBR style + rendering, or FLAT, for flat shading, or PHONG for older biased + rendering workflows. Based on the chosen lighting model, you may then + specify the appropriate colour maps, such as diffuse colours, + specularity, emissive component, etc. These lighting models are fully + compatible with glTF and X3D. This should be used if your model is + prepared to be rendered by a rendering engine which is compatible with + glTF / X3D shader descriptions. If you are doing archviz or 3D + rendering, this is what you need. + - Textures, this is a special type of Rendering presentation item that + uses image textures instead of single colours. Textures may be either + mapped using a bounding box stretch mapping, or with UV coordinates + for mesh-like geometry. + - Lighting, this is used to define photometrically accurate colour + parameters used in lighting simulation. If you are a simulationist, + this is what you need. + - Reflectance, this is a special type of Lighting presentation item + which includes some lesser used photometric properties, typically + required for advanced materials like glazing. + - External, this is for any other surface style defined using an + external URI. This is relevant if you are using a third-party non-glTF + compatible shader definition such as for Cycles, Renderman, V-Ray, + etc, or a complex lighting simulation definition, such as for + Radiance. - Shading is sufficient for the majority of basic models. + Shading is sufficient for the majority of basic models. - The attributes you specify will depend on the type of presentation item - you are adding. An example is shown below, but for full details please - refer to the IFC documentation. + The attributes you specify will depend on the type of presentation item + you are adding. An example is shown below, but for full details please + refer to the IFC documentation. - :param style: The IfcSurfaceStyle you want to add to presentation item - to. See ifcopenshell.api.style.add_style. - :type style: ifcopenshell.entity_instance - :param ifc_class: Choose from IfcSurfaceStyleShading, - IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, - IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or - IfcExternallyDefinedSurfaceStyle. - :type ifc_class: str - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: The newly created presentation item based on the provided - ifc_class. - :rtype: ifcopenshell.entity_instance + :param style: The IfcSurfaceStyle you want to add to presentation item + to. See ifcopenshell.api.style.add_style. + :type style: ifcopenshell.entity_instance + :param ifc_class: Choose from IfcSurfaceStyleShading, + IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, + IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or + IfcExternallyDefinedSurfaceStyle. + :type ifc_class: str + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: The newly created presentation item based on the provided + ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Create a simple shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + # Create a simple shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) - # Alternatively, create a rendering style. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleRendering", attributes={ - # A surface colour and transparency is still supplied for - # viewport display only. This will supersede the shading - # presentation item. - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent + # Alternatively, create a rendering style. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleRendering", attributes={ + # A surface colour and transparency is still supplied for + # viewport display only. This will supersede the shading + # presentation item. + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent - # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting - # model. In IFC4X3, you may choose PHYSICAL directly. - "ReflectanceMethod": "NOTDEFINED", + # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting + # model. In IFC4X3, you may choose PHYSICAL directly. + "ReflectanceMethod": "NOTDEFINED", - # For PBR shading, you may specify these parameters: - "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, - "SpecularColour": 0.1, # Metallic factor - "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor - }) - """ - self.file = file - self.settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}} + # For PBR shading, you may specify these parameters: + "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, + "SpecularColour": 0.1, # Metallic factor + "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor + }) + """ + settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}} - def execute(self): - style_item = self.file.create_entity(self.settings["ifc_class"]) - ifcopenshell.api.run( - "style.edit_surface_style", self.file, style=style_item, attributes=self.settings["attributes"] - ) - styles = list(self.settings["style"].Styles or []) + style_item = file.create_entity(settings["ifc_class"]) + ifcopenshell.api.run("style.edit_surface_style", file, style=style_item, attributes=settings["attributes"]) + styles = list(settings["style"].Styles or []) - select_class = self.settings["ifc_class"] - if select_class == "IfcSurfaceStyleRendering": - select_class = "IfcSurfaceStyleShading" - duplicate_items = [s for s in styles if s.is_a(select_class)] - for duplicate_item in duplicate_items: - ifcopenshell.api.run("style.remove_surface_style", self.file, style=duplicate_item) + select_class = settings["ifc_class"] + if select_class == "IfcSurfaceStyleRendering": + select_class = "IfcSurfaceStyleShading" + duplicate_items = [s for s in styles if s.is_a(select_class)] + for duplicate_item in duplicate_items: + ifcopenshell.api.run("style.remove_surface_style", file, style=duplicate_item) - styles = list(self.settings["style"].Styles or []) - styles.append(style_item) - self.settings["style"].Styles = styles - return style_item + styles = list(settings["style"].Styles or []) + styles.append(style_item) + settings["style"].Styles = styles + return style_item diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py index 88aabfe801..9b6fbda053 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py @@ -20,39 +20,42 @@ import ifcopenshell import ifcopenshell.api +def add_surface_textures(file, material=None, uv_maps=None, textures=None) -> None: + """Add surface texture based on a Blender material definition or texture data. + + :param material: The Blender material definition with a node tree that + is compatible with glTF. See one of the valid combinations here: + https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html + :type material: bpy.types.Material + :param uv_maps: A list of IfcIndexedTextureMap for any + IfcTessellatedFaceSets that the representation has, obtained from + the HasTextures attribute. + :type uv_maps: list[ifcopenshell.entity_instance] + :param textures: A list of dictionaries containing: + + 1. Attributes to create IfcImageTexture. + 2. One additional parameter `uv_mode` to map IfcImageTexture to correct + IfcTextureCoordinate type. + + Possible `uv_mode` values: + + * `UV` - use IfcTextureCoordinate from `uv_maps` parameter; + * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV + based on geometry); + * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV + based on camera position) + :type textures: list[dict] + :return: A list of IfcImageTexture + :rtype: list[ifcopenshell.entity_instance] + """ + usecase = Usecase() + # TODO: This usecase currently depends on Blender's data model + usecase.file = file + usecase.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []} + return usecase.execute() + + class Usecase: - def __init__(self, file, material=None, uv_maps=None, textures=None): - """Add surface texture based on a Blender material definition or texture data. - - :param material: The Blender material definition with a node tree that - is compatible with glTF. See one of the valid combinations here: - https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html - :type material: bpy.types.Material - :param uv_maps: A list of IfcIndexedTextureMap for any - IfcTessellatedFaceSets that the representation has, obtained from - the HasTextures attribute. - :type uv_maps: list[ifcopenshell.entity_instance] - :param textures: A list of dictionaries containing: - - 1. Attributes to create IfcImageTexture. - 2. One additional parameter `uv_mode` to map IfcImageTexture to correct - IfcTextureCoordinate type. - - Possible `uv_mode` values: - - * `UV` - use IfcTextureCoordinate from `uv_maps` parameter; - * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV - based on geometry); - * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV - based on camera position) - :type textures: list[dict] - :return: A list of IfcImageTexture - :rtype: list[ifcopenshell.entity_instance] - """ - # TODO: This usecase currently depends on Blender's data model - self.file = file - self.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []} - def execute(self): if self.file.schema == "IFC2X3": # TODO: research how compatible IFC2X3 and IFC4 textures are diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py index 06d3a6339a..630a842bf6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py @@ -21,91 +21,96 @@ import ifcopenshell.api import ifcopenshell.util.element +def assign_material_style( + file, material=None, style=None, context=None, should_use_presentation_style_assignment=False +) -> None: + """Assigns a style to a material + + A style may either be assigned directly to an object's representation, + or to a material which is then associated with the object. If both + exist, then the style assigned directly to the object's representation + takes precedence. It is recommended to use materials and assign styles + to materials. This API function provides that capability. + + :param material: The IfcMaterial which you want to assign the style to. + :type material: ifcopenshell.entity_instance + :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that + you want to assign to the material. This will then be applied to all + objects that have that material. + :type style: ifcopenshell.entity_instance + :param context: The IfcGeometricRepresentationSubContext at which this + style should be used. Typically this is the Model BODY context. + :type context: ifcopenshell.entity_instance + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: None + :rtype: None + + Example: + + .. code:: python + + # A model context is needed to store 3D geometry + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # Specifically, we want to store body geometry + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Let's prepare a concrete material. Note that our concrete material + # does not have any colours (styles) at this point. + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Assign our concrete material to our wall + ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterial", material=concrete) + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a simple grey shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) + + # Now any element (like our wall) with a concrete material will have + # a grey colour applied. + ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "material": material, + "style": style, + "context": context, + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, material=None, style=None, context=None, should_use_presentation_style_assignment=False): - """Assigns a style to a material - - A style may either be assigned directly to an object's representation, - or to a material which is then associated with the object. If both - exist, then the style assigned directly to the object's representation - takes precedence. It is recommended to use materials and assign styles - to materials. This API function provides that capability. - - :param material: The IfcMaterial which you want to assign the style to. - :type material: ifcopenshell.entity_instance - :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that - you want to assign to the material. This will then be applied to all - objects that have that material. - :type style: ifcopenshell.entity_instance - :param context: The IfcGeometricRepresentationSubContext at which this - style should be used. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: None - :rtype: None - - Example: - - .. code:: python - - # A model context is needed to store 3D geometry - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # Specifically, we want to store body geometry - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - - # Let's prepare a concrete material. Note that our concrete material - # does not have any colours (styles) at this point. - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Assign our concrete material to our wall - ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterial", material=concrete) - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a simple grey shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) - - # Now any element (like our wall) with a concrete material will have - # a grey colour applied. - ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body) - """ - self.file = file - self.settings = { - "material": material, - "style": style, - "context": context, - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): self.style = self.settings["style"] if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py index c6236c8a4a..e2f5daf766 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py @@ -17,98 +17,100 @@ # along with IfcOpenShell. If not, see . +def assign_representation_styles( + file, + shape_representation=None, + styles=None, + replace_previous_same_type_style=True, + should_use_presentation_style_assignment=False, +) -> None: + """Assigns a style directly to an object representation + + A style may either be assigned directly to an object's representation, + or to a material which is then associated with the object. If both + exist, then the style assigned directly to the object's representation + takes precedence. It is recommended to use materials and assign styles + to materials. However, sometimes you may want to assign colours directly + to the object representation as an override. This API function provides + that capability. + + If you want to assign styles to a material instead (recommended), then + please see ifcopenshell.api.style.assign_material_style. + + :param shape_representation: The IfcShapeRepresentation of the object + that you want to assign styles to. This implicitly defines the + context at which the styles should be used. + :type shape_representation: ifcopenshell.entity_instance + :param styles: A list of presentation styles, typically IfcSurfaceStyle. + The number of items in the list should correlate with the number of + items in the shape_representation's Items attribute. If you have + more items than styles, the last style is used. + :type styles: list[ifcopenshell.entity_instance] + :param replace_previous_same_type_style: Remove previously assigned styles + of the same type as currently assign style`. Defaults to `True`. + :type replace_previous_same_type_style: bool + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: List of created IfcStyledItems + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A model context is needed to store 3D geometry + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # Specifically, we want to store body geometry + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a simple grey shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) + + # Now specifically our wall only will be coloured grey. + ifcopenshell.api.run("style.assign_representation_styles", model, + shape_representation=representation, styles=[style]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "shape_representation": shape_representation, + "styles": styles or [], + "replace_previous_same_type_style": replace_previous_same_type_style, + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - shape_representation=None, - styles=None, - replace_previous_same_type_style=True, - should_use_presentation_style_assignment=False, - ): - """Assigns a style directly to an object representation - - A style may either be assigned directly to an object's representation, - or to a material which is then associated with the object. If both - exist, then the style assigned directly to the object's representation - takes precedence. It is recommended to use materials and assign styles - to materials. However, sometimes you may want to assign colours directly - to the object representation as an override. This API function provides - that capability. - - If you want to assign styles to a material instead (recommended), then - please see ifcopenshell.api.style.assign_material_style. - - :param shape_representation: The IfcShapeRepresentation of the object - that you want to assign styles to. This implicitly defines the - context at which the styles should be used. - :type shape_representation: ifcopenshell.entity_instance - :param styles: A list of presentation styles, typically IfcSurfaceStyle. - The number of items in the list should correlate with the number of - items in the shape_representation's Items attribute. If you have - more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance] - :param replace_previous_same_type_style: Remove previously assigned styles - of the same type as currently assign style`. Defaults to `True`. - :type replace_previous_same_type_style: bool - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: List of created IfcStyledItems - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A model context is needed to store 3D geometry - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # Specifically, we want to store body geometry - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a simple grey shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) - - # Now specifically our wall only will be coloured grey. - ifcopenshell.api.run("style.assign_representation_styles", model, - shape_representation=representation, styles=[style]) - """ - self.file = file - self.settings = { - "shape_representation": shape_representation, - "styles": styles or [], - "replace_previous_same_type_style": replace_previous_same_type_style, - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): if not self.settings["styles"]: return [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py index 877d0f89c2..268acfdc2e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, style=None, attributes=None): - """Edits the attributes of an IfcPresentationStyle +def edit_presentation_style(file, style=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationStyle - For more information about the attributes and data types of an - IfcPresentationStyle, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPresentationStyle, consult the IFC documentation. - :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param style: The IfcPresentationStyle entity you want to edit + :type style: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Change the name of the style to "Foo" - ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"style": style, "attributes": attributes or {}} + # Change the name of the style to "Foo" + ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"}) + """ + settings = {"style": style, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["style"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["style"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py index 20c1002fdf..b8c7a30be8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py @@ -17,61 +17,64 @@ # along with IfcOpenShell. If not, see . +def edit_surface_style(file, style=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationItem + + For more information about the attributes and data types of an + IfcPresentationItem, consult the IFC documentation. + + The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading, + IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, + IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or + IfcExternallyDefinedSurfaceStyle. + + To represent a colour, a nested dictionary should be used. See the + example below. + + :param style: The IfcPresentationStyle entity you want to edit + :type style: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a blank rendering style. + rendering = ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleRendering") + + # Edit the attributes of the rendering style. + ifcopenshell.api.run("style.edit_surface_style", model, + style=rendering, attributes={ + # A surface colour and transparency is still supplied for + # viewport display only. This will supersede the shading + # presentation item. + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + + # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting + # model. In IFC4X3, you may choose PHYSICAL directly. + "ReflectanceMethod": "NOTDEFINED", + + # For PBR shading, you may specify these parameters: + "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, + "SpecularColour": 0.1, # Metallic factor + "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor + }) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"style": style, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, style=None, attributes=None): - """Edits the attributes of an IfcPresentationItem - - For more information about the attributes and data types of an - IfcPresentationItem, consult the IFC documentation. - - The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading, - IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, - IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or - IfcExternallyDefinedSurfaceStyle. - - To represent a colour, a nested dictionary should be used. See the - example below. - - :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a blank rendering style. - rendering = ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleRendering") - - # Edit the attributes of the rendering style. - ifcopenshell.api.run("style.edit_surface_style", model, - style=rendering, attributes={ - # A surface colour and transparency is still supplied for - # viewport display only. This will supersede the shading - # presentation item. - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - - # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting - # model. In IFC4X3, you may choose PHYSICAL directly. - "ReflectanceMethod": "NOTDEFINED", - - # For PBR shading, you may specify these parameters: - "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, - "SpecularColour": 0.1, # Metallic factor - "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor - }) - """ - self.file = file - self.settings = {"style": style, "attributes": attributes or {}} - def execute(self): attributes = {} for attribute in self.settings["style"].wrapped_data.declaration().as_entity().all_attributes(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py index 40692982bb..453f511f2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py @@ -19,30 +19,33 @@ import ifcopenshell.util.element +def remove_style(file, style=None) -> None: + """Removes a presentation style + + All of the presentation items of the style will also be removed. + + :param style: The IfcPresentationStyle to remove. + :type style: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Not anymore! + ifcopenshell.api.run("style.remove_style", model, style=style) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"style": style} + return usecase.execute() + + class Usecase: - def __init__(self, file, style=None): - """Removes a presentation style - - All of the presentation items of the style will also be removed. - - :param style: The IfcPresentationStyle to remove. - :type style: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Not anymore! - ifcopenshell.api.run("style.remove_style", model, style=style) - """ - self.file = file - self.settings = {"style": style} - def execute(self): self.purge_styled_items(self.settings["style"]) for style in self.settings["style"].Styles or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py index ab061e182c..62ab7e4973 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, representation=None): - """Removes a styled representation +def remove_styled_representation(file, representation=None) -> None: + """Removes a styled representation - Styled representations are typically associated with materials. This - removes the representation but not the underlying styles. + Styled representations are typically associated with materials. This + removes the representation but not the underlying styles. - :param representation: The IfcStyledRepresentation to remove. - :type representation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param representation: The IfcStyledRepresentation to remove. + :type representation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remove a styled representation - ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation) - """ - self.file = file - self.settings = {"representation": representation} + # Remove a styled representation + ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation) + """ + settings = {"representation": representation} - def execute(self): - for inverse in self.file.get_inverse(self.settings["representation"]): - if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1: - self.file.remove(inverse) + for inverse in file.get_inverse(settings["representation"]): + if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1: + file.remove(inverse) - for item in self.settings["representation"].Items: - if item.is_a("IfcStyledItem") and self.file.get_total_inverses(item) == 1: - for style in item.Styles: - if style.is_a("IfcPresentationStyleAssignment"): - self.file.remove(style) - self.file.remove(item) + for item in settings["representation"].Items: + if item.is_a("IfcStyledItem") and file.get_total_inverses(item) == 1: + for style in item.Styles: + if style.is_a("IfcPresentationStyleAssignment"): + file.remove(style) + file.remove(item) - self.file.remove(self.settings["representation"]) + file.remove(settings["representation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py index ce214dbf21..9621d5b51a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py @@ -20,50 +20,47 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, style=None): - """Removes a presentation item from a presentation style +def remove_surface_style(file, style=None) -> None: + """Removes a presentation item from a presentation style - :param style: The IfcPresentationItem to remove. - :type style: ifcopenshell.entity_instance - :return: None - :rtype: None + :param style: The IfcPresentationItem to remove. + :type style: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Create a simple shading colour and transparency. - shading = ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + # Create a simple shading colour and transparency. + shading = ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) - # Remove the shading item - ifcopenshell.api.run("style.remove_surface_style", model, style=shading) - """ - self.file = file - self.settings = {"style": style} + # Remove the shading item + ifcopenshell.api.run("style.remove_surface_style", model, style=shading) + """ + settings = {"style": style} - def execute(self): - to_delete = set() - if self.settings["style"].is_a("IfcSurfaceStyleWithTextures"): - for texture in self.settings["style"].Textures or []: - if texture.IsMappedBy: - for coordinate in texture.IsMappedBy: - to_delete.add(coordinate) - else: - to_delete.add(texture) + to_delete = set() + if settings["style"].is_a("IfcSurfaceStyleWithTextures"): + for texture in settings["style"].Textures or []: + if texture.IsMappedBy: + for coordinate in texture.IsMappedBy: + to_delete.add(coordinate) + else: + to_delete.add(texture) - for attribute in self.settings["style"]: - if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id(): - to_delete.add(attribute) + for attribute in settings["style"]: + if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id(): + to_delete.add(attribute) - self.file.remove(self.settings["style"]) + file.remove(settings["style"]) - for element in to_delete: - ifcopenshell.util.element.remove_deep2(self.file, element) + for element in to_delete: + ifcopenshell.util.element.remove_deep2(file, element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py index f1e2e7e85b..59b37a1935 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py @@ -20,78 +20,75 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material=None, style=None, context=None): - """Unassigns a style to a material +def unassign_material_style(file, material=None, style=None, context=None) -> None: + """Unassigns a style to a material - This does the inverse of assign_material_style. + This does the inverse of assign_material_style. - :param material: The IfcMaterial which you want to unassign the style from. - :type material: ifcopenshell.entity_instance - :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that - you want to unassign from material. This will then be applied to all - objects that have that material. - :type style: ifcopenshell.entity_instance - :param context: The IfcGeometricRepresentationSubContext at which this - style should be unassigned. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterial which you want to unassign the style from. + :type material: ifcopenshell.entity_instance + :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that + you want to unassign from material. This will then be applied to all + objects that have that material. + :type style: ifcopenshell.entity_instance + :param context: The IfcGeometricRepresentationSubContext at which this + style should be unassigned. Typically this is the Model BODY context. + :type context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body) - """ - self.file = file - self.settings = { - "material": material, - "style": style, - "context": context, - } + ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body) + """ + settings = { + "material": material, + "style": style, + "context": context, + } - def execute(self): - for definition in self.settings["material"].HasRepresentation: - for representation in definition.Representations: - if not representation.is_a("IfcStyledRepresentation"): - continue - if representation.ContextOfItems != self.settings["context"]: - continue - for item in representation.Items: - if not item.is_a("IfcStyledItem"): - continue - styles = [s for s in item.Styles if s != self.settings["style"]] - if not styles: - self.file.remove(item) - elif len(styles) != len(item.Styles): - item.Styles = styles - if not representation.Items: - self.file.remove(representation) - if not definition.Representations: - self.file.remove(definition) - - # handle material constituents and shape aspects - material_constituents_names = [] - for inverse in self.file.get_inverse(self.settings["material"]): - if inverse.is_a("IfcMaterialConstituent") and inverse.Name: - material_constituents_names.append(inverse.Name) - if not material_constituents_names: - return - - elements = ifcopenshell.util.element.get_elements_by_material(self.file, self.settings["material"]) - shape_aspects = [] - for element in elements: - shape_aspects += ifcopenshell.util.element.get_shape_aspects(element) - - for shape_aspect in shape_aspects: - if shape_aspect.Name not in material_constituents_names: + for definition in settings["material"].HasRepresentation: + for representation in definition.Representations: + if not representation.is_a("IfcStyledRepresentation"): continue + if representation.ContextOfItems != settings["context"]: + continue + for item in representation.Items: + if not item.is_a("IfcStyledItem"): + continue + styles = [s for s in item.Styles if s != settings["style"]] + if not styles: + file.remove(item) + elif len(styles) != len(item.Styles): + item.Styles = styles + if not representation.Items: + file.remove(representation) + if not definition.Representations: + file.remove(definition) - for rep in shape_aspect.ShapeRepresentations: - ifcopenshell.api.run( - "style.unassign_representation_styles", - self.file, - shape_representation=rep, - styles=[self.settings["style"]], - ) + # handle material constituents and shape aspects + material_constituents_names = [] + for inverse in file.get_inverse(settings["material"]): + if inverse.is_a("IfcMaterialConstituent") and inverse.Name: + material_constituents_names.append(inverse.Name) + if not material_constituents_names: + return + + elements = ifcopenshell.util.element.get_elements_by_material(file, settings["material"]) + shape_aspects = [] + for element in elements: + shape_aspects += ifcopenshell.util.element.get_shape_aspects(element) + + for shape_aspect in shape_aspects: + if shape_aspect.Name not in material_constituents_names: + continue + + for rep in shape_aspect.ShapeRepresentations: + ifcopenshell.api.run( + "style.unassign_representation_styles", + file, + shape_representation=rep, + styles=[settings["style"]], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py index 83f60fe3d4..14f52e9c6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py @@ -17,43 +17,48 @@ # along with IfcOpenShell. If not, see . +def unassign_representation_styles( + file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False +) -> None: + """Unassigns styles directly assigned to an object representation + + This does the inverse of assign_representation_styles. + + :param shape_representation: The IfcShapeRepresentation of the object + that you want to unassign styles from. + :type shape_representation: ifcopenshell.entity_instance + :param styles: A list of presentation styles, typically IfcSurfaceStyle. + The number of items in the list should correlate with the number of + items in the shape_representation's Items attribute. If you have + more items than styles, the last style is used. + :type styles: list[ifcopenshell.entity_instance] + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("style.unassign_representation_styles", model, + shape_representation=representation, styles=[style]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "shape_representation": shape_representation, + "styles": styles or [], + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False): - """Unassigns styles directly assigned to an object representation - - This does the inverse of assign_representation_styles. - - :param shape_representation: The IfcShapeRepresentation of the object - that you want to unassign styles from. - :type shape_representation: ifcopenshell.entity_instance - :param styles: A list of presentation styles, typically IfcSurfaceStyle. - The number of items in the list should correlate with the number of - items in the shape_representation's Items attribute. If you have - more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance] - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("style.unassign_representation_styles", model, - shape_representation=representation, styles=[style]) - """ - self.file = file - self.settings = { - "shape_representation": shape_representation, - "styles": styles or [], - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): if not self.settings["styles"]: return [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py index e0caddbe3c..14213ca168 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_port import add_port +from .add_system import add_system +from .assign_flow_control import assign_flow_control +from .assign_port import assign_port +from .assign_system import assign_system +from .connect_port import connect_port +from .disconnect_port import disconnect_port +from .edit_system import edit_system +from .remove_system import remove_system +from .unassign_flow_control import unassign_flow_control +from .unassign_port import unassign_port +from .unassign_system import unassign_system diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py index f792ecc2fa..a3664cffbb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py @@ -20,44 +20,41 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, element=None): - """Adds a new distribution port to an element +def add_port(file, element=None) -> None: + """Adds a new distribution port to an element - A distribution port represents a connection point on an element, where - a distribution element may be connected to another distribution element. - For example, a duct segment will typically have two ports, one at either - end, because you can attach another segment or fitting to either end of - the duct segment. + A distribution port represents a connection point on an element, where + a distribution element may be connected to another distribution element. + For example, a duct segment will typically have two ports, one at either + end, because you can attach another segment or fitting to either end of + the duct segment. - This will both add a distribution port and automatically assign it to a - distribution element. + This will both add a distribution port and automatically assign it to a + distribution element. - :param element: The IfcDistributionElement you want to add a - distribution port to. - :type element: ifcopenshell.entity_instance - :return: The newly created IfcDistributionPort - :rtype: ifcopenshell.entity_instance + :param element: The IfcDistributionElement you want to add a + distribution port to. + :type element: ifcopenshell.entity_instance + :return: The newly created IfcDistributionPort + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - """ - self.file = file - self.settings = { - "element": element, - } + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + """ + settings = { + "element": element, + } - def execute(self): - port = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDistributionPort") - if self.settings["element"]: - ifcopenshell.api.run("system.assign_port", self.file, element=self.settings["element"], port=port) - return port + port = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcDistributionPort") + if settings["element"]: + ifcopenshell.api.run("system.assign_port", file, element=settings["element"], port=port) + return port diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py index 26c8cfe8fc..75027ee55a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py @@ -20,45 +20,42 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem"): - """Add a new distribution system +def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem") -> ifcopenshell.entity_instance: + """Add a new distribution system - A distribution system is a group of distribution elements, like ducts, - pipes, pumps, filters, fans, and so on that distribute a medium (air, - liquid, or electricity) throughout a facility. Systems may be - hierarchical, with larger systems composed of smaller subsystems. + A distribution system is a group of distribution elements, like ducts, + pipes, pumps, filters, fans, and so on that distribute a medium (air, + liquid, or electricity) throughout a facility. Systems may be + hierarchical, with larger systems composed of smaller subsystems. - :param ifc_class: The type of system, chosen from IfcDistributionSystem - for mechanical, electrical, communications, plumbing, fire, or - security systems. Alternatively you may choose IfcBuildingSystem for - specialised building facade systems or similar. For IFC2X3, choose - IfcSystem. - :type ifc_class: str - :return: The newly created IfcSystem. - :rtype: ifcopenshell.entity_instance + :param ifc_class: The type of system, chosen from IfcDistributionSystem + for mechanical, electrical, communications, plumbing, fire, or + security systems. Alternatively you may choose IfcBuildingSystem for + specialised building facade systems or similar. For IFC2X3, choose + IfcSystem. + :type ifc_class: str + :return: The newly created IfcSystem. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) - """ - self.file = file - self.settings = {"ifc_class": ifc_class} + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + """ + settings = {"ifc_class": ifc_class} - def execute(self) -> ifcopenshell.entity_instance: - ifc_class = self.settings["ifc_class"] - # workaround for failing default argument in ifc2x3 - if self.file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem": - ifc_class = "IfcSystem" + ifc_class = settings["ifc_class"] + # workaround for failing default argument in ifc2x3 + if file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem": + ifc_class = "IfcSystem" - return self.file.create_entity( - ifc_class, - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": "Unnamed", - } - ) + return file.create_entity( + ifc_class, + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": "Unnamed", + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py index aae80ab6eb..2254a43dec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py @@ -20,66 +20,63 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_flow_element=None, related_flow_control=None): - """Assigns to the flow element control element that either sense or control - some aspect of the flow element. +def assign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None: + """Assigns to the flow element control element that either sense or control + some aspect of the flow element. - Note that control can be assigned only to the one flow element. + Note that control can be assigned only to the one flow element. - :param related_flow_control: IfcDistributionControlElement - which may be used to impart control on the flow element - :type related_flow_control: ifcopenshell.entity_instance - :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed - :type relating_flow_element: ifcopenshell.entity_instance - :return: Matching or newly created IfcRelFlowControlElements. If control - is already assigned to some other element method will return None. - :rtype: ifcopenshell.entity_instance, None + :param related_flow_control: IfcDistributionControlElement + which may be used to impart control on the flow element + :type related_flow_control: ifcopenshell.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed + :type relating_flow_element: ifcopenshell.entity_instance + :return: Matching or newly created IfcRelFlowControlElements. If control + is already assigned to some other element method will return None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - flow_element = model.createIfcFlowSegment() - flow_control = model.createIfcController() - relation = ifcopenshell.api.run( - "system.assign_flow_control", model, - related_flow_control=flow_control, relating_flow_element=flow_element - ) - """ - self.file = file - self.settings = { - "relating_flow_element": relating_flow_element, - "related_flow_control": related_flow_control, - } + flow_element = model.createIfcFlowSegment() + flow_control = model.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", model, + related_flow_control=flow_control, relating_flow_element=flow_element + ) + """ + settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } - def execute(self): - if self.settings["related_flow_control"].AssignedToFlowElement: - # only 1 control per 1 flow element is possible - assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] - if assignment.RelatingFlowElement == self.settings["relating_flow_element"]: - return assignment - # return None if this control is already assigned to another flow element - return + if settings["related_flow_control"].AssignedToFlowElement: + # only 1 control per 1 flow element is possible + assignment = settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement == settings["relating_flow_element"]: + return assignment + # return None if this control is already assigned to another flow element + return - if self.settings["relating_flow_element"].HasControlElements: - assignment = self.settings["relating_flow_element"].HasControlElements[0] - if self.settings["related_flow_control"] in assignment.RelatedControlElements: - return assignment - - related_flow_controls = set(assignment.RelatedControlElements) - related_flow_controls.add(self.settings["related_flow_control"]) - assignment.RelatedControlElements = list(related_flow_controls) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) + if settings["relating_flow_element"].HasControlElements: + assignment = settings["relating_flow_element"].HasControlElements[0] + if settings["related_flow_control"] in assignment.RelatedControlElements: return assignment - assignment = self.file.create_entity( - "IfcRelFlowControlElements", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedControlElements": [self.settings["related_flow_control"]], - "RelatingFlowElement": self.settings["relating_flow_element"], - }, - ) + related_flow_controls = set(assignment.RelatedControlElements) + related_flow_controls.add(settings["related_flow_control"]) + assignment.RelatedControlElements = list(related_flow_controls) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment}) return assignment + + assignment = file.create_entity( + "IfcRelFlowControlElements", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedControlElements": [settings["related_flow_control"]], + "RelatingFlowElement": settings["relating_flow_element"], + }, + ) + return assignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py index 728a935395..c424f8eb4b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py @@ -21,46 +21,49 @@ import ifcopenshell.api import ifcopenshell.util.placement +def assign_port(file, element=None, port=None) -> None: + """Assigns a port to an element + + If you have an orphaned port, you may assign it to a distribution + element using this function. Ports should typically not be orphaned, but + it may be useful when patching up models. + + :param element: The IfcDistributionElement to assign the port to. + :type element: ifcopenshell.entity_instance + :param port: The IfcDistributionPort you want to assign. + :type port: ifcopenshell.entity_instance + :return: The IfcRelNests relationship, or the + IfcRelConnectsPortToElement for IFC2X3. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + + # Reassign it back + ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "port": port, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, element=None, port=None): - """Assigns a port to an element - - If you have an orphaned port, you may assign it to a distribution - element using this function. Ports should typically not be orphaned, but - it may be useful when patching up models. - - :param element: The IfcDistributionElement to assign the port to. - :type element: ifcopenshell.entity_instance - :param port: The IfcDistributionPort you want to assign. - :type port: ifcopenshell.entity_instance - :return: The IfcRelNests relationship, or the - IfcRelConnectsPortToElement for IFC2X3. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - - # Unassign one port for some weird reason. - ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) - - # Reassign it back - ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1) - """ - self.file = file - self.settings = { - "element": element, - "port": port, - } - def execute(self): if self.file.schema == "IFC2X3": return self.execute_ifc2x3() diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py index 20f5a8519f..e0b19c5165 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py @@ -21,51 +21,47 @@ import ifcopenshell.api import ifcopenshell.util.system -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - system: ifcopenshell.entity_instance, - ): - """Assigns distribution elements to a system +def assign_system( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + system: ifcopenshell.entity_instance, +) -> None: + """Assigns distribution elements to a system - Note that it is not necessary to assign distribution ports to a system. + Note that it is not necessary to assign distribution ports to a system. - :param products: The list of IfcDistributionElements to assign to the system. - :type products: list[ifcopenshell.entity_instance] - :param system: The IfcSystem you want to assign the element to. - :type system: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - or `None` if `products` was empty list. - :rtype: [ifcopenshell.entity_instance, None] + :param products: The list of IfcDistributionElements to assign to the system. + :type products: list[ifcopenshell.entity_instance] + :param system: The IfcSystem you want to assign the element to. + :type system: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + or `None` if `products` was empty list. + :rtype: [ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # This duct is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - """ - self.file = file - self.settings = { - "products": products, - "system": system, - } + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + """ + settings = { + "products": products, + "system": system, + } - def execute(self): - system = self.settings["system"] - products = self.settings["products"] + system = settings["system"] + products = settings["products"] - if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products): - raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}") + if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products): + raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}") - rel = ifcopenshell.api.run("group.assign_group", self.file, products=products, group=system) - return rel + rel = ifcopenshell.api.run("group.assign_group", file, products=products, group=system) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py index 7d23dde1a7..f5773c5a21 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py @@ -21,84 +21,87 @@ import ifcopenshell.api import ifcopenshell.util.element +def connect_port(file, port1=None, port2=None, direction="NOTDEFINED", element=None) -> None: + """Connects two ports together + + A distribution element (e.g. a duct) may be connected to another + distribution element (e.g. a fitting) by connecting a port at one of the + duct to a port at the same end of the fitting. + + Ports may only have one connection, so you cannot have multiple things + connected to the same port. Nor can you have incompatible port + connections, such as an electrical port connected to an airflow port. + + Port connectivity may be explicit or implicit. Explicit connections are + where the port connectivity is described for every single distribution + element in detail. For example, a duct segment would have port + connections to a duct fitting, which would have port connections to + another duct segment, all the way from a fan to an air terminal exactly + as constructed on site. Implicit connections only consider the key + distribution control elements (e.g. the fan and the terminal) and ignore + all of the details of the duct segments and fittings in between. + Generally, explicit connectivity is preferred for later detailed design, + and implicit connectivity is preferred for early phase design. + + :param port1: The port of the first distribution element to connect. + :type port1: ifcopenshell.entity_instance + :param port2: The port of the second distribution element to connect. + :type port2: ifcopenshell.entity_instance + :param direction: The directionality of distribution flow through the + port connection. NOTDEFINED means that the direction has not yet + been determined. This is useful during preliminary system design. + SOURCE means that the flow is from the first element to the second + element. SINK means that the flow is from the second element to the + first element. SOURCEANDSINK means that flow is bi-directional + between the first and second element. SOURCEANDSINK is a relatively + rare scenario. + :type direction: str + :param element: Optionally set an element through which the port + connectivity is made, such as a segment or fitting. This is only to + be used for implicit port connectivity where the segments and + fittings are less important. + :type element: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") + + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) + + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) + + # Connect the duct and fitting together. At this point, we have not + # yet determined the direction of the flow, so we leave direction as + # NOTDEFINED. + ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "port1": port1, + "port2": port2, + "direction": direction, + "element": element, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, port1=None, port2=None, direction="NOTDEFINED", element=None): - """Connects two ports together - - A distribution element (e.g. a duct) may be connected to another - distribution element (e.g. a fitting) by connecting a port at one of the - duct to a port at the same end of the fitting. - - Ports may only have one connection, so you cannot have multiple things - connected to the same port. Nor can you have incompatible port - connections, such as an electrical port connected to an airflow port. - - Port connectivity may be explicit or implicit. Explicit connections are - where the port connectivity is described for every single distribution - element in detail. For example, a duct segment would have port - connections to a duct fitting, which would have port connections to - another duct segment, all the way from a fan to an air terminal exactly - as constructed on site. Implicit connections only consider the key - distribution control elements (e.g. the fan and the terminal) and ignore - all of the details of the duct segments and fittings in between. - Generally, explicit connectivity is preferred for later detailed design, - and implicit connectivity is preferred for early phase design. - - :param port1: The port of the first distribution element to connect. - :type port1: ifcopenshell.entity_instance - :param port2: The port of the second distribution element to connect. - :type port2: ifcopenshell.entity_instance - :param direction: The directionality of distribution flow through the - port connection. NOTDEFINED means that the direction has not yet - been determined. This is useful during preliminary system design. - SOURCE means that the flow is from the first element to the second - element. SINK means that the flow is from the second element to the - first element. SOURCEANDSINK means that flow is bi-directional - between the first and second element. SOURCEANDSINK is a relatively - rare scenario. - :type direction: str - :param element: Optionally set an element through which the port - connectivity is made, such as a segment or fitting. This is only to - be used for implicit port connectivity where the segments and - fittings are less important. - :type element: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) - - # Create a duct and a 90 degree bend fitting - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - fitting = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctFitting", predefined_type="BEND") - - # The duct and fitting is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) - - # Create 2 ports, one for either end of both the duct and fitting. - duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) - fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) - - # Connect the duct and fitting together. At this point, we have not - # yet determined the direction of the flow, so we leave direction as - # NOTDEFINED. - ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) - """ - self.file = file - self.settings = { - "port1": port1, - "port2": port2, - "direction": direction, - "element": element, - } - def execute(self): # Note: there are a number of ambiguities with port connectivity. We # assume system topology is represented by a directed graph. In other diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py index 071074e9e7..15d5890493 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py @@ -21,63 +21,60 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, port=None): - """Disconnects a port from any other port +def disconnect_port(file, port=None) -> None: + """Disconnects a port from any other port - A port may only be connected to one other port, so the other port is not - needed to be specified. + A port may only be connected to one other port, so the other port is not + needed to be specified. - :param port: The IfcDistributionPort to disconnect. - :type port: ifcopenshell.entity_instance - :return: None - :rtype: None + :param port: The IfcDistributionPort to disconnect. + :type port: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct and a 90 degree bend fitting - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - fitting = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctFitting", predefined_type="BEND") + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") - # The duct and fitting is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) - # Create 2 ports, one for either end of both the duct and fitting. - duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) - fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) - # Connect the duct and fitting together. At this point, we have not - # yet determined the direction of the flow, so we leave direction as - # NOTDEFINED. - ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) + # Connect the duct and fitting together. At this point, we have not + # yet determined the direction of the flow, so we leave direction as + # NOTDEFINED. + ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) - # Disconnect the port. note we could've equally disconnected - # fitting_port1 instead of duct_port2 - ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2) - """ - self.file = file - self.settings = { - "port": port, - } + # Disconnect the port. note we could've equally disconnected + # fitting_port1 instead of duct_port2 + ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2) + """ + settings = { + "port": port, + } - def execute(self): - rels = self.settings["port"].ConnectedTo or () - rels += self.settings["port"].ConnectedFrom or () + rels = settings["port"].ConnectedTo or () + rels += settings["port"].ConnectedFrom or () - for rel in rels: - rel.RelatingPort.FlowDirection = None - rel.RelatedPort.FlowDirection = None - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + rel.RelatingPort.FlowDirection = None + rel.RelatedPort.FlowDirection = None + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py index 315c04ccd5..83fd250ddd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, system=None, attributes=None): - """Edits the attributes of an IfcSystem +def edit_system(file, system=None, attributes=None) -> None: + """Edits the attributes of an IfcSystem - For more information about the attributes and data types of an - IfcSystem, consult the IFC documentation. + For more information about the attributes and data types of an + IfcSystem, consult the IFC documentation. - :param system: The IfcSystem entity you want to edit - :type system: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param system: The IfcSystem entity you want to edit + :type system: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Change the name of the system to "HW" for Hot Water - ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"}) - """ + # Change the name of the system to "HW" for Hot Water + ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"}) + """ - self.file = file - self.settings = {"system": system, "attributes": attributes or {}} + settings = {"system": system, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["system"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["system"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py index f331a5f3e3..a81a06127f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py @@ -21,55 +21,52 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, system=None): - """Removes a distribution system +def remove_system(file, system=None) -> None: + """Removes a distribution system - All the distribution elements within the system are retained. + All the distribution elements within the system are retained. - :param system: The IfcSystem to remove. - :type system: ifcopenshell.entity_instance - :return: None - :rtype: None + :param system: The IfcSystem to remove. + :type system: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Delete it. - ifcopenshell.api.run("system.remove_system", model, system=system) - """ - self.file = file - self.settings = {"system": system} + # Delete it. + ifcopenshell.api.run("system.remove_system", model, system=system) + """ + settings = {"system": system} - def execute(self): - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["system"])]: - try: - inverse = self.file.by_id(inverse_id) - except: - continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["system"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToGroup"): - if inverse.RelatingGroup == self.settings["system"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["system"].OwnerHistory - self.file.remove(self.settings["system"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for inverse_id in [i.id() for i in file.get_inverse(settings["system"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["system"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToGroup"): + if inverse.RelatingGroup == settings["system"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["system"].OwnerHistory + file.remove(settings["system"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py index 04eda27f83..feba961f1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py @@ -21,57 +21,54 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_flow_element=None, related_flow_control=None): - """Unassigns flow control element from the flow element. +def unassign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None: + """Unassigns flow control element from the flow element. - :param related_flow_control: IfcDistributionControlElement controling the - flow element - :type related_flow_control: ifcopenshell.entity_instance - :param relating_flow_element: The IfcDistributionFlowElement that is being controlled - :type relating_flow_element: ifcopenshell.entity_instance - :return: If the control still is related to other objects, the - IfcRelFlowControlElements is returned, otherwise None. - :rtype: ifcopenshell.entity_instance, None + :param related_flow_control: IfcDistributionControlElement controling the + flow element + :type related_flow_control: ifcopenshell.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled + :type relating_flow_element: ifcopenshell.entity_instance + :return: If the control still is related to other objects, the + IfcRelFlowControlElements is returned, otherwise None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # assign control to the flow element - flow_element = self.file.createIfcFlowSegment() - flow_control = self.file.createIfcController() - relation = ifcopenshell.api.run( - "system.assign_flow_control", self.file, - relating_control=flow_control, related_object=flow_element - ) + # assign control to the flow element + flow_element = file.createIfcFlowSegment() + flow_control = file.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", file, + relating_control=flow_control, related_object=flow_element + ) - # und unassign it - ifcopenshell.api.run("system.unassign_flow_control", self.file, - relating_control=flow_control, related_object=flow_element - ) - """ + # und unassign it + ifcopenshell.api.run("system.unassign_flow_control", file, + relating_control=flow_control, related_object=flow_element + ) + """ - self.file = file - self.settings = { - "relating_flow_element": relating_flow_element, - "related_flow_control": related_flow_control, - } + settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } - def execute(self): - if not self.settings["related_flow_control"].AssignedToFlowElement: - return - assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] - if assignment.RelatingFlowElement != self.settings["relating_flow_element"]: - return - if len(assignment.RelatedControlElements) == 1: - history = assignment.OwnerHistory - self.file.remove(assignment) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_flow_controls = list(assignment.RelatedControlElements) - related_flow_controls.remove(self.settings["related_flow_control"]) - assignment.RelatedControlElements = related_flow_controls - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) - return assignment + if not settings["related_flow_control"].AssignedToFlowElement: + return + assignment = settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement != settings["relating_flow_element"]: + return + if len(assignment.RelatedControlElements) == 1: + history = assignment.OwnerHistory + file.remove(assignment) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_flow_controls = list(assignment.RelatedControlElements) + related_flow_controls.remove(settings["related_flow_control"]) + assignment.RelatedControlElements = related_flow_controls + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment}) + return assignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py index e9d82722aa..678c086140 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py @@ -20,42 +20,45 @@ import ifcopenshell import ifcopenshell.api +def unassign_port(file, element=None, port=None) -> None: + """Unassigns a port to an element + + Ports are typically always assigned to a distribution element, but in + some edge cases you may want to unassign the port to create an orphaned + port for cleaning or patchin purposes. + + :param element: The IfcDistributionElement to unassign the port from. + :type element: ifcopenshell.entity_instance + :param port: The IfcDistributionPort you want to unassign. + :type port: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "port": port, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, element=None, port=None): - """Unassigns a port to an element - - Ports are typically always assigned to a distribution element, but in - some edge cases you may want to unassign the port to create an orphaned - port for cleaning or patchin purposes. - - :param element: The IfcDistributionElement to unassign the port from. - :type element: ifcopenshell.entity_instance - :param port: The IfcDistributionPort you want to unassign. - :type port: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - - # Unassign one port for some weird reason. - ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) - """ - self.file = file - self.settings = { - "element": element, - "port": port, - } - def execute(self): if self.file.schema == "IFC2X3": return self.execute_ifc2x3() diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py index fbc3dd854b..7bb82aebb7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py @@ -21,46 +21,40 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - system: ifcopenshell.entity_instance, - ): - """Unassigns list of products from a system +def unassign_system( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + system: ifcopenshell.entity_instance, +) -> None: + """Unassigns list of products from a system - :param products: The list of IfcDistributionElements to unassign from the system. - :type products: list[ifcopenshell.entity_instance] - :param system: The IfcSystem you want to unassign the element from. - :type system: ifcopenshell.entity_instance - :return: None - :rtype: None + :param products: The list of IfcDistributionElements to unassign from the system. + :type products: list[ifcopenshell.entity_instance] + :param system: The IfcSystem you want to unassign the element from. + :type system: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # This duct is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - # Not anymore! - ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system) - """ - self.file = file - self.settings = { - "products": products, - "system": system, - } + # Not anymore! + ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system) + """ + settings = { + "products": products, + "system": system, + } - def execute(self): - ifcopenshell.api.run( - "group.unassign_group", self.file, products=self.settings["products"], group=self.settings["system"] - ) + ifcopenshell.api.run("group.unassign_group", file, products=settings["products"], group=settings["system"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py index e0caddbe3c..dddd90a49f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_type import assign_type +from .get_related_objects import get_related_objects +from .map_type_representations import map_type_representations +from .unassign_type import unassign_type diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index b8d7cf357a..9d30d6083a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -22,166 +22,168 @@ import ifcopenshell.util.element from typing import Union, Iterable +def assign_type( + file: ifcopenshell.file, + related_objects: list[ifcopenshell.entity_instance], + relating_type: ifcopenshell.entity_instance, + should_map_representations=True, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns a type to occurrences of an object + + IFC supports the concept of occurrences and types. An occurrence is an + actual physical product in the real world: like a wall, a chair, a door, + a column, a pump, and so on. + + Most occurrences have a corresponding type. A type describes either a + common shape and set of properties of a particular model of equipment, + or a construction typology. An occurrence may only have zero or one + type. + + For example, architects would typically have a door schedule for + individual occurrences of doors and a door types schedule for a handful + of door types, described by the door hardware, frame, and panel. Other + examples might be window types or wall types. Structural engineers would + have a list of column types, beam types, slab types, etc, such as a 400 + diameter column, a 500 diameter column, and so on. Services consultant + might nominate a particular type of sprinkler which have many + occurrences, or light fixture types, and so on. + + Types are critical as they communicate to the procurement team what + types of equipment and products need to be procured. The individual + occurrences of that type tell them how many to procure. Types are also + critical in construction as they indicate succinctly how to manufacture + or construct something. For example, a wall type is enough information + for a builder to understand the build up and construction of a wall. + Types are used to help break down cost plans, or isolate portions of an + assembly process for construction scheduling. Types are also used in + facility maintenance, as occurrences sharing the same type can be + repaired in the same way or by replacing the same parts. + + An occurrence of a type inherits all the properties and materials of the + type. For example, a 2HR fire rated wall type implies that all + wall occurrences of that wall type will also be 2HR fire rated. + + A type may or may not have a geometric representation. If a type does + not have any representation, then the occurrences are free to have any + representation of their own. However, if a type has a representation, + all occurrences must have the same representation. For example, if a + light fixture downlight type has a representation of a cylinder, then + all occurrences must have exactly the same cylinder as its + representation. If you change the cylinder's shape of the type, then all + occurrence representations will also change. + + If a type does not have any geometric representation, they may have a + parametric material representation. This may be either a parametric + layered material or parametric cross-sectional profile material. If this + is the case, the occurrence must be constructed out of the parametric + material. For example, if a wall type uses a list of parametric layers + indicating a thickness of 13mm plasterboard and 90mm stud, then the + thickness of every wall occurrence representation must be 103mm. The + length of each wall, however, may vary. Similarly, if a beam type has a + parametric profile material of an I-beam, then all beam occurrences must + also be this I-beam shape, though the length may vary. + + It is highly recommended for every occurrence to have a type. There are + some exceptions to the rule, such as in heritage architecture or + as-built or dilapidation models, where existing conditions are + ambiguous, unknown or are so bespoke as to have no logical type. + + :param related_objects: The IfcElement occurrences. + :type related_objects: list[ifcopenshell.entity_instance] + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :param should_map_representations: If a type has a representation map, + IFC requires all occurrences to map those representations. Some IFC + vendors might disobey this, or you might want to handle it + yourusecase. In this scenario, you may set this to False. + This also enabled adding material usages mapping. + :type should_map_representations: bool + :return: The IfcRelDefinesByType relationship + or `None` if `related_objects` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] + + Example: + + .. code:: python + + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") + + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + + # Assign the furniture to the furniture type. If the furniture_type + # had a representation, the furniture occurrence will also now have + # the exact same representation. This is highly efficient as you + # don't need to define the representation for every occurrence. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + + # Let's imagine a parametric material layer set + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) + + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + + # Now, let's create a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The wall is a WAL01 wall type. + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.118) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "related_objects": related_objects, + "relating_type": relating_type, + "should_map_representations": should_map_representations, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_objects: list[ifcopenshell.entity_instance], - relating_type: ifcopenshell.entity_instance, - should_map_representations=True, - ): - """Assigns a type to occurrences of an object - - IFC supports the concept of occurrences and types. An occurrence is an - actual physical product in the real world: like a wall, a chair, a door, - a column, a pump, and so on. - - Most occurrences have a corresponding type. A type describes either a - common shape and set of properties of a particular model of equipment, - or a construction typology. An occurrence may only have zero or one - type. - - For example, architects would typically have a door schedule for - individual occurrences of doors and a door types schedule for a handful - of door types, described by the door hardware, frame, and panel. Other - examples might be window types or wall types. Structural engineers would - have a list of column types, beam types, slab types, etc, such as a 400 - diameter column, a 500 diameter column, and so on. Services consultant - might nominate a particular type of sprinkler which have many - occurrences, or light fixture types, and so on. - - Types are critical as they communicate to the procurement team what - types of equipment and products need to be procured. The individual - occurrences of that type tell them how many to procure. Types are also - critical in construction as they indicate succinctly how to manufacture - or construct something. For example, a wall type is enough information - for a builder to understand the build up and construction of a wall. - Types are used to help break down cost plans, or isolate portions of an - assembly process for construction scheduling. Types are also used in - facility maintenance, as occurrences sharing the same type can be - repaired in the same way or by replacing the same parts. - - An occurrence of a type inherits all the properties and materials of the - type. For example, a 2HR fire rated wall type implies that all - wall occurrences of that wall type will also be 2HR fire rated. - - A type may or may not have a geometric representation. If a type does - not have any representation, then the occurrences are free to have any - representation of their own. However, if a type has a representation, - all occurrences must have the same representation. For example, if a - light fixture downlight type has a representation of a cylinder, then - all occurrences must have exactly the same cylinder as its - representation. If you change the cylinder's shape of the type, then all - occurrence representations will also change. - - If a type does not have any geometric representation, they may have a - parametric material representation. This may be either a parametric - layered material or parametric cross-sectional profile material. If this - is the case, the occurrence must be constructed out of the parametric - material. For example, if a wall type uses a list of parametric layers - indicating a thickness of 13mm plasterboard and 90mm stud, then the - thickness of every wall occurrence representation must be 103mm. The - length of each wall, however, may vary. Similarly, if a beam type has a - parametric profile material of an I-beam, then all beam occurrences must - also be this I-beam shape, though the length may vary. - - It is highly recommended for every occurrence to have a type. There are - some exceptions to the rule, such as in heritage architecture or - as-built or dilapidation models, where existing conditions are - ambiguous, unknown or are so bespoke as to have no logical type. - - :param related_objects: The IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance] - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :param should_map_representations: If a type has a representation map, - IFC requires all occurrences to map those representations. Some IFC - vendors might disobey this, or you might want to handle it - yourself. In this scenario, you may set this to False. - This also enabled adding material usages mapping. - :type should_map_representations: bool - :return: The IfcRelDefinesByType relationship - or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] - - Example: - - .. code:: python - - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") - - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - - # Assign the furniture to the furniture type. If the furniture_type - # had a representation, the furniture occurrence will also now have - # the exact same representation. This is highly efficient as you - # don't need to define the representation for every occurrence. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - - # Let's imagine a parametric material layer set - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) - - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - - # Now, let's create a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # The wall is a WAL01 wall type. - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.118) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = { - "related_objects": related_objects, - "relating_type": relating_type, - "should_map_representations": should_map_representations, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): if not self.settings["related_objects"]: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py index 0a05de4118..3610f27af5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py @@ -19,43 +19,40 @@ import ifcopenshell -class Usecase: - def __init__(self, file, related_object=None, relating_type=None): - """Gets all the related occurrences of a type +def get_related_objects(file, related_object=None, relating_type=None) -> None: + """Gets all the related occurrences of a type - Do not use this function. It will be removed. Use - ifcopenshell.util.element.get_type or - ifcopenshell.util.element.get_types instead. + Do not use this function. It will be removed. Use + ifcopenshell.util.element.get_type or + ifcopenshell.util.element.get_types instead. - :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :return: A list of occurrences of the type. - :rtype: list[ifcopenshell.entity_instance] - """ - self.file = file - self.settings = { - "related_object": related_object, - "relating_type": relating_type, - } + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :return: A list of occurrences of the type. + :rtype: list[ifcopenshell.entity_instance] + """ + settings = { + "related_object": related_object, + "relating_type": relating_type, + } - def execute(self): - if self.settings["related_object"]: - if self.file.schema == "IFC2X3": - is_defined_by = self.settings["related_object"].IsDefinedBy - for rel in is_defined_by: - if rel.is_a("IfcRelDefinesByType"): - return set([int(o.id()) for o in rel.RelatedObjects]) - else: - is_typed_by = self.settings["related_object"].IsTypedBy - if is_typed_by: - return set([int(o.id()) for o in is_typed_by[0].RelatedObjects]) - elif self.settings["relating_type"]: - if self.file.schema == "IFC2X3": - types = self.settings["relating_type"].ObjectTypeOf - else: - types = self.settings["relating_type"].Types - if types: - return set([int(o.id()) for o in types[0].RelatedObjects]) - return set() + if settings["related_object"]: + if file.schema == "IFC2X3": + is_defined_by = settings["related_object"].IsDefinedBy + for rel in is_defined_by: + if rel.is_a("IfcRelDefinesByType"): + return set([int(o.id()) for o in rel.RelatedObjects]) + else: + is_typed_by = settings["related_object"].IsTypedBy + if is_typed_by: + return set([int(o.id()) for o in is_typed_by[0].RelatedObjects]) + elif settings["relating_type"]: + if file.schema == "IFC2X3": + types = settings["relating_type"].ObjectTypeOf + else: + types = settings["relating_type"].Types + if types: + return set([int(o.id()) for o in types[0].RelatedObjects]) + return set() diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py index 064fb148bb..59c1655c6f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py @@ -21,99 +21,93 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_object: ifcopenshell.entity_instance, - relating_type: ifcopenshell.entity_instance, - ): - """Ensures that all occurrences has the same representation as the type +def map_type_representations( + file: ifcopenshell.file, + related_object: ifcopenshell.entity_instance, + relating_type: ifcopenshell.entity_instance, +) -> None: + """Ensures that all occurrences has the same representation as the type - If a type has a representation, all occurrences must have the same - representation. If the type's representation changes, this function may - be used to ensure consistency of the occurrence's representations. + If a type has a representation, all occurrences must have the same + representation. If the type's representation changes, this function may + be used to ensure consistency of the occurrence's representations. - :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :return: None - :rtype: None + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # Place our furniture at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture) + # Place our furniture at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture) - # Assign the furniture to the furniture type. Right now, the - # furniture type has no representation, so the furniture may also - # have no representation, or any arbitrary representation that may - # vary from occurrence to occurrence. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + # Assign the furniture to the furniture type. Right now, the + # furniture type has no representation, so the furniture may also + # have no representation, or any arbitrary representation that may + # vary from occurrence to occurrence. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our furniture type. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our furniture type. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Let's create a mesh representation of an arbitrary 2m cube. - representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body, - vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0), - (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]], - faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]]) + # Let's create a mesh representation of an arbitrary 2m cube. + representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body, + vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0), + (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]], + faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]]) - # Assign our new body geometry back to our furniture type. In this - # case, since we use the API, all occurrences automatically get the - # representation mapped, so there is nothing more we need to do. - ifcopenshell.api.run("geometry.assign_representation", model, - product=furniture_type, representation=representation) + # Assign our new body geometry back to our furniture type. In this + # case, since we use the API, all occurrences automatically get the + # representation mapped, so there is nothing more we need to do. + ifcopenshell.api.run("geometry.assign_representation", model, + product=furniture_type, representation=representation) - # However, if you were doing some sort of manual IFC patching, like - # assigning furniture_type.RepresentationMaps directly, then you - # might make this call: - # ifcopenshell.api.run("type.map_type_representations", model, - # related_object=furniture, relating_type=furniture_type) - """ - self.file = file - self.settings = { - "related_object": related_object, - "relating_type": relating_type, - } + # However, if you were doing some sort of manual IFC patching, like + # assigning furniture_type.RepresentationMaps directly, then you + # might make this call: + # ifcopenshell.api.run("type.map_type_representations", model, + # related_object=furniture, relating_type=furniture_type) + """ + settings = { + "related_object": related_object, + "relating_type": relating_type, + } - def execute(self) -> None: - if not self.settings["relating_type"].RepresentationMaps: - return - representations = [] - if self.settings["related_object"].Representation: - representations = self.settings["related_object"].Representation.Representations - for representation in representations: - ifcopenshell.api.run( - "geometry.unassign_representation", - self.file, - product=self.settings["related_object"], - representation=representation, - ) - ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation}) - for representation_map in self.settings["relating_type"].RepresentationMaps: - representation = representation_map.MappedRepresentation - mapped_representation = ifcopenshell.api.run( - "geometry.map_representation", self.file, representation=representation - ) - ifcopenshell.api.run( - "geometry.assign_representation", - self.file, - product=self.settings["related_object"], - representation=mapped_representation, - ) + if not settings["relating_type"].RepresentationMaps: + return + representations = [] + if settings["related_object"].Representation: + representations = settings["related_object"].Representation.Representations + for representation in representations: + ifcopenshell.api.run( + "geometry.unassign_representation", + file, + product=settings["related_object"], + representation=representation, + ) + ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation}) + for representation_map in settings["relating_type"].RepresentationMaps: + representation = representation_map.MappedRepresentation + mapped_representation = ifcopenshell.api.run("geometry.map_representation", file, representation=representation) + ifcopenshell.api.run( + "geometry.assign_representation", + file, + product=settings["related_object"], + representation=mapped_representation, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py index a629100477..cbc41a7785 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py @@ -21,58 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]): - """Unassigns a type from occurrences +def unassign_type(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None: + """Unassigns a type from occurrences - Note that unassigning a type doesn't automatically remove mapped representations - and material usages associated with the previously assigned type. + Note that unassigning a type doesn't automatically remove mapped representations + and material usages associated with the previously assigned type. - :param related_objects: List of IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param related_objects: List of IfcElement occurrences. + :type related_objects: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # Assign the furniture to the furniture type. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + # Assign the furniture to the furniture type. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - # Change our mind. Maybe it's a different type? - ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture]) - """ - self.file = file - self.settings = {"related_objects": related_objects} + # Change our mind. Maybe it's a different type? + ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture]) + """ + settings = {"related_objects": related_objects} - def execute(self) -> None: - related_objects = set(self.settings["related_objects"]) + related_objects = set(settings["related_objects"]) - if self.file.schema == "IFC2X3": - rels = set( - rel - for object in related_objects - if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None)) - ) + if file.schema == "IFC2X3": + rels = set( + rel + for object in related_objects + if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None)) + ) + else: + rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None))) + + for rel in rels: + related_objects = set(rel.RelatedObjects) - related_objects + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None))) - - for rel in rels: - related_objects = set(rel.RelatedObjects) - related_objects - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py index e0caddbe3c..3813724dd7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py @@ -15,3 +15,14 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_context_dependent_unit import add_context_dependent_unit +from .add_conversion_based_unit import add_conversion_based_unit +from .add_monetary_unit import add_monetary_unit +from .add_si_unit import add_si_unit +from .assign_unit import assign_unit +from .edit_derived_unit import edit_derived_unit +from .edit_monetary_unit import edit_monetary_unit +from .edit_named_unit import edit_named_unit +from .remove_unit import remove_unit +from .unassign_unit import unassign_unit diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py index a0d705a94b..5de43a505a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None): - """Add a new arbitrary unit that can only be interpreted in a project specific context +def add_context_dependent_unit(file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None) -> None: + """Add a new arbitrary unit that can only be interpreted in a project specific context - Occasionally the construction industry uses arbitrary units to quantify - objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings - or equipment. + Occasionally the construction industry uses arbitrary units to quantify + objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings + or equipment. - :param unit_type: Typically should be left as USERDEFINED, unless for - some bizarre reason you are redefining something you could use a - sensible normal unit for. In that case, firstly stop whatever you're - doing and have a hard think about your life, and then if life really - is going that badly for you, check out the IFC docs for IfcUnitEnum. - :type unit_type: str - :param name: Give your unit a name. X what? X bananas? - :type name: str - :param dimensions: Units typically measure one of 7 fundamental physical - dimensions: length, mass, time, electric current, temperature, - substance amount, or luminous intensity. These are represented as a - list of 7 integers, representing the exponents of each one of these - dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0), - where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per - second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is - recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0). - :type dimensions: list[int] - :return: The new IfcContextDependentUnit - :rtype: ifcopenshell.entity_instance + :param unit_type: Typically should be left as USERDEFINED, unless for + some bizarre reason you are redefining something you could use a + sensible normal unit for. In that case, firstly stop whatever you're + doing and have a hard think about your life, and then if life really + is going that badly for you, check out the IFC docs for IfcUnitEnum. + :type unit_type: str + :param name: Give your unit a name. X what? X bananas? + :type name: str + :param dimensions: Units typically measure one of 7 fundamental physical + dimensions: length, mass, time, electric current, temperature, + substance amount, or luminous intensity. These are represented as a + list of 7 integers, representing the exponents of each one of these + dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0), + where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per + second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is + recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0). + :type dimensions: list[int] + :return: The new IfcContextDependentUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Boxes of things - ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") - """ - self.file = file - self.settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)} + # Boxes of things + ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") + """ + settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)} - def execute(self): - return self.file.create_entity( - "IfcContextDependentUnit", - Dimensions=self.file.createIfcDimensionalExponents(*self.settings["dimensions"]), - UnitType=self.settings["unit_type"], - Name=self.settings["name"], - ) + return file.create_entity( + "IfcContextDependentUnit", + Dimensions=file.createIfcDimensionalExponents(*settings["dimensions"]), + UnitType=settings["unit_type"], + Name=settings["name"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py index 20b96d3298..b87011b2e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py @@ -21,67 +21,66 @@ import ifcopenshell.util.unit from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None): - """Add a conversion based unit +def add_conversion_based_unit( + file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None +) -> ifcopenshell.entity_instance: + """Add a conversion based unit - If you're in one of those countries who don't use SI units, you're - probably simply using SI units converted into another unit. If you want - to use _those_ units, you can create a conversion based unit with this - function. You can choose from one of: inch, foot, yard, mile, square - inch, square foot, square yard, acre, square mile, cubic inch, cubic - foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint - US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf, - kip, psi, ksi, minute, hour, day, btu, and fahrenheit. + If you're in one of those countries who don't use SI units, you're + probably simply using SI units converted into another unit. If you want + to use _those_ units, you can create a conversion based unit with this + function. You can choose from one of: inch, foot, yard, mile, square + inch, square foot, square yard, acre, square mile, cubic inch, cubic + foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint + US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf, + kip, psi, ksi, minute, hour, day, btu, and fahrenheit. - :param name: A converted name chosen from the list above. - :type name: str - :param conversion_offset: If you want to offset the conversion further - by a set number, you may specify it here. For example, fahrenheit is - 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note - that this is just an example and you don't actually need to specify - that for fahrenheit as it's built into this API function. For - advanced users only. - :type conversion_offset: float, optional - :return: The new IfcConversionBasedUnit or - IfcConversionBasedUnitWithOffset - :rtype: ifcopenshell.entity_instance + :param name: A converted name chosen from the list above. + :type name: str + :param conversion_offset: If you want to offset the conversion further + by a set number, you may specify it here. For example, fahrenheit is + 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note + that this is just an example and you don't actually need to specify + that for fahrenheit as it's built into this API function. For + advanced users only. + :type conversion_offset: float, optional + :return: The new IfcConversionBasedUnit or + IfcConversionBasedUnitWithOffset + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Some common imperial measurements - length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch") - area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot") + # Some common imperial measurements + length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch") + area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot") - # Make it our default units, if we are doing an imperial building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - """ - self.file = file - self.settings = {"name": name, "conversion_offset": conversion_offset} + # Make it our default units, if we are doing an imperial building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + """ + settings = {"name": name, "conversion_offset": conversion_offset} - def execute(self) -> ifcopenshell.entity_instance: - unit_type = ifcopenshell.util.unit.imperial_types.get(self.settings["name"], "USERDEFINED") - dimensions = ifcopenshell.util.unit.named_dimensions[unit_type] - exponents = self.file.createIfcDimensionalExponents(*dimensions) - si_name = ifcopenshell.util.unit.si_type_names[unit_type] - si_unit = self.file.createIfcSIUnit(UnitType=unit_type, Name=si_name) + unit_type = ifcopenshell.util.unit.imperial_types.get(settings["name"], "USERDEFINED") + dimensions = ifcopenshell.util.unit.named_dimensions[unit_type] + exponents = file.createIfcDimensionalExponents(*dimensions) + si_name = ifcopenshell.util.unit.si_type_names[unit_type] + si_unit = file.createIfcSIUnit(UnitType=unit_type, Name=si_name) - conversion_real = ifcopenshell.util.unit.si_conversions.get(self.settings["name"], 1) - value_component = self.file.create_entity("IfcReal", **{"wrappedValue": conversion_real}) - conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit) + conversion_real = ifcopenshell.util.unit.si_conversions.get(settings["name"], 1) + value_component = file.create_entity("IfcReal", **{"wrappedValue": conversion_real}) + conversion_factor = file.createIfcMeasureWithUnit(value_component, si_unit) - conversion_offset = self.settings["conversion_offset"] - if not conversion_offset: - conversion_offset = ifcopenshell.util.unit.si_offsets.get(self.settings["name"], 0) + conversion_offset = settings["conversion_offset"] + if not conversion_offset: + conversion_offset = ifcopenshell.util.unit.si_offsets.get(settings["name"], 0) - if conversion_offset: - return self.file.createIfcConversionBasedUnitWithOffset( - exponents, - unit_type, - self.settings["name"], - conversion_factor, - conversion_offset, - ) - return self.file.createIfcConversionBasedUnit(exponents, unit_type, self.settings["name"], conversion_factor) + if conversion_offset: + return file.createIfcConversionBasedUnitWithOffset( + exponents, + unit_type, + settings["name"], + conversion_factor, + conversion_offset, + ) + return file.createIfcConversionBasedUnit(exponents, unit_type, settings["name"], conversion_factor) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py index f15b18ab91..7e345b9a7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, currency="DOLLARYDOO"): - """Add a new currency +def add_monetary_unit(file, currency="DOLLARYDOO") -> None: + """Add a new currency - Currency units are useful in cost plans to know in what currency the - costs are calculated in. The currencies should follow ISO 4217, like - USD, GBP, AUD, MYR, etc. + Currency units are useful in cost plans to know in what currency the + costs are calculated in. The currencies should follow ISO 4217, like + USD, GBP, AUD, MYR, etc. - :param currency: The currency code - :type currency: str - :return: The newly created IfcMonetaryUnit - :rtype: ifcopenshell.entity_instance + :param currency: The currency code + :type currency: str + :return: The newly created IfcMonetaryUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # If you do all your cost plans in Zimbabwean dollars then nobody - # knows how accurate the numbers are. - zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") + # If you do all your cost plans in Zimbabwean dollars then nobody + # knows how accurate the numbers are. + zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") - # Make it our default currency - ifcopenshell.api.run("unit.assign_unit", model, units=[zwl]) - """ - self.file = file - self.settings = {"currency": currency} + # Make it our default currency + ifcopenshell.api.run("unit.assign_unit", model, units=[zwl]) + """ + settings = {"currency": currency} - def execute(self): - return self.file.create_entity("IfcMonetaryUnit", self.settings["currency"]) + return file.create_entity("IfcMonetaryUnit", settings["currency"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py index 7eb8019632..67ce025dd8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py @@ -20,48 +20,45 @@ import ifcopenshell.util.unit from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None): - """Add a new SI unit +def add_si_unit( + file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None +) -> ifcopenshell.entity_instance: + """Add a new SI unit - The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT, - AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT, - ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT, - ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT, - FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT, - LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT, - MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT, - RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT, - TIMEUNIT, VOLUMEUNIT. + The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT, + AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT, + ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT, + ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT, + FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT, + LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT, + MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT, + RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT, + TIMEUNIT, VOLUMEUNIT. - Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO, - KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA. + Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO, + KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA. - :param unit_type: A type of unit chosen from the list above. For - example, choosing LENGTHUNIT will give you a metre. - :type unit_type: str - :param prefix: A prefix chosen from the list above, or None for no - prefix. - :type prefix: str,optional - :return: The newly created IfcSIUnit - :rtype: ifcopenshell.entity_instance + :param unit_type: A type of unit chosen from the list above. For + example, choosing LENGTHUNIT will give you a metre. + :type unit_type: str + :param prefix: A prefix chosen from the list above, or None for no + prefix. + :type prefix: str,optional + :return: The newly created IfcSIUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - """ - self.file = file - self.settings = {"unit_type": unit_type, "prefix": prefix} + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + """ + settings = {"unit_type": unit_type, "prefix": prefix} - def execute(self) -> ifcopenshell.entity_instance: - name = ifcopenshell.util.unit.si_type_names.get(self.settings["unit_type"], None) - return self.file.create_entity( - "IfcSIUnit", UnitType=self.settings["unit_type"], Name=name, Prefix=self.settings["prefix"] - ) + name = ifcopenshell.util.unit.si_type_names.get(settings["unit_type"], None) + return file.create_entity("IfcSIUnit", UnitType=settings["unit_type"], Name=name, Prefix=settings["prefix"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index 67305295bd..1e6e1a9cdf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -21,61 +21,63 @@ import ifcopenshell.util.unit from typing import Optional +def assign_unit( + file: ifcopenshell.file, + units: Optional[list[ifcopenshell.entity_instance]] = None, + length: Optional[dict] = None, + area: Optional[dict] = None, + volume: Optional[dict] = None, +) -> ifcopenshell.entity_instance: + """Assign default project units + + Whenever a unitised quantity is specified, such as a length, area, + voltage, pressure, etc, these project units are used by default. + + It is also possible to override units for specific properties. For + example, generally you might want square metres for area measurements, + but you might want square millimeters for the measurements of the cross + sectional area of cables in cable trays. However, this function only + deals with the default project units. + + :param units: A list of units to assign as project defaults. See + ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit, + and unit.add_monetary_unit for information on how to create units. + :type units: list[ifcopenshell.entity_instance],optional + :return: The IfcUnitAssignment element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # You need a project before you can assign units. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + + # Alternatively, you may specify without any arguments to + # automatically create millimeters, square meters, and cubic meters + # as a convenience for testing purposes. Sorry imperial folks, we + # prioritise metric here. + ifcopenshell.api.run("unit.assign_unit", model) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"units": units} + # This is a convenience function, likely to be deprecated in the future. + usecase.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"} + usecase.settings["area"] = area or {"is_metric": True, "raw": "METERS"} + usecase.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - units: Optional[list[ifcopenshell.entity_instance]] = None, - length: Optional[dict] = None, - area: Optional[dict] = None, - volume: Optional[dict] = None, - ): - """Assign default project units - - Whenever a unitised quantity is specified, such as a length, area, - voltage, pressure, etc, these project units are used by default. - - It is also possible to override units for specific properties. For - example, generally you might want square metres for area measurements, - but you might want square millimeters for the measurements of the cross - sectional area of cables in cable trays. However, this function only - deals with the default project units. - - :param units: A list of units to assign as project defaults. See - ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit, - and unit.add_monetary_unit for information on how to create units. - :type units: list[ifcopenshell.entity_instance],optional - :return: The IfcUnitAssignment element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # You need a project before you can assign units. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - - # Alternatively, you may specify without any arguments to - # automatically create millimeters, square meters, and cubic meters - # as a convenience for testing purposes. Sorry imperial folks, we - # prioritise metric here. - ifcopenshell.api.run("unit.assign_unit", model) - """ - self.file = file - self.settings = {"units": units} - # This is a convenience function, likely to be deprecated in the future. - self.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"} - self.settings["area"] = area or {"is_metric": True, "raw": "METERS"} - self.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"} - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): # We're going to refactor this to split unit creation and assignment if self.settings["units"]: units = self.settings["units"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py index 636430159c..ed56b80461 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcDerivedUnit +def edit_derived_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcDerivedUnit - For more information about the attributes and data types of an - IfcDerivedUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDerivedUnit, consult the IFC documentation. - :param unit: The IfcDerivedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + :param unit: The IfcDerivedUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py index aee4b89305..b4f14f328a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcMonetaryUnit +def edit_monetary_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcMonetaryUnit - For more information about the attributes and data types of an - IfcMonetaryUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMonetaryUnit, consult the IFC documentation. - :param unit: The IfcMonetaryUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param unit: The IfcMonetaryUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # If you do all your cost plans in Zimbabwean dollars then nobody - # knows how accurate the numbers are. - zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") + # If you do all your cost plans in Zimbabwean dollars then nobody + # knows how accurate the numbers are. + zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") - # Ah who are we kidding - ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"}) - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + # Ah who are we kidding + ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"}) + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py index da4ff5290f..be0384aabb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py @@ -17,44 +17,41 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcNamedUnit +def edit_named_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcNamedUnit - Named units include SI units, conversion based units (imperial units), - and context dependent units. + Named units include SI units, conversion based units (imperial units), + and context dependent units. - For more information about the attributes and data types of an - IfcNamedUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcNamedUnit, consult the IFC documentation. - :param unit: The IfcNamedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param unit: The IfcNamedUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Boxes of things - unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") + # Boxes of things + unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") - # Uh, crates? Boxes? Whatever. - ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"}) - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + # Uh, crates? Boxes? Whatever. + ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"}) + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "Dimensions": - dimensions = self.settings["unit"].Dimensions - if len(self.file.get_inverse(dimensions)) > 1: - self.settings["unit"].Dimensions = self.file.createIfcDimensionalExponents(*value) - else: - for i, exponent in enumerate(value): - dimensions[i] = exponent - continue - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + if name == "Dimensions": + dimensions = settings["unit"].Dimensions + if len(file.get_inverse(dimensions)) > 1: + settings["unit"].Dimensions = file.createIfcDimensionalExponents(*value) + else: + for i, exponent in enumerate(value): + dimensions[i] = exponent + continue + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py index ba9aff862b..ae2cd192cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py @@ -20,38 +20,35 @@ import ifcopenshell.util.unit import ifcopenshell.util.element -class Usecase: - def __init__(self, file, unit=None): - """Remove a unit +def remove_unit(file, unit=None) -> None: + """Remove a unit - Be very careful when a unit is removed, as it may mean that previously - defined quantities in the model completely lose their meaning. + Be very careful when a unit is removed, as it may mean that previously + defined quantities in the model completely lose their meaning. - :param unit: The unit element to remove - :type unit: ifcopenshell.entity_instance - :return: None - :rtype: None + :param unit: The unit element to remove + :type unit: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # What? - unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS") + # What? + unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS") - # Yeah maybe not. - ifcopenshell.api.run("unit.remove_unit", model, unit=unit) - """ - self.file = file - self.settings = {"unit": unit} + # Yeah maybe not. + ifcopenshell.api.run("unit.remove_unit", model, unit=unit) + """ + settings = {"unit": unit} - def execute(self): - unit_assignment = ifcopenshell.util.unit.get_unit_assignment(self.file) - if unit_assignment and self.settings["unit"] in unit_assignment.Units: - units = list(unit_assignment.Units) - units.remove(self.settings["unit"]) - if units: - unit_assignment.Units = units - else: - self.file.remove(unit_assignment) - ifcopenshell.util.element.remove_deep(self.file, self.settings["unit"]) + unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file) + if unit_assignment and settings["unit"] in unit_assignment.Units: + units = list(unit_assignment.Units) + units.remove(settings["unit"]) + if units: + unit_assignment.Units = units + else: + file.remove(unit_assignment) + ifcopenshell.util.element.remove_deep(file, settings["unit"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py index 2da27bd08c..0c0b41c2f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py @@ -19,43 +19,40 @@ import ifcopenshell from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None): - """Unassigns units as default units for the project +def unassign_unit(file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None) -> None: + """Unassigns units as default units for the project - :param units: A list of units to assign as project defaults. - :type units: list[ifcopenshell.entity_instance],optional - :return: None - :rtype: None + :param units: A list of units to assign as project defaults. + :type units: list[ifcopenshell.entity_instance],optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # You need a project before you can assign units. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + # You need a project before you can assign units. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - # Actually, we don't need areas. - ifcopenshell.api.run("unit.unassign_unit", model, units=[area]) - """ - self.file = file - self.settings = {"units": units} + # Actually, we don't need areas. + ifcopenshell.api.run("unit.unassign_unit", model, units=[area]) + """ + settings = {"units": units} - def execute(self): - unit_assignment = self.file.by_type("IfcUnitAssignment") - if not unit_assignment: - return - unit_assignment = unit_assignment[0] - units = set(unit_assignment.Units or []) - units = units - set(self.settings["units"]) - if units: - unit_assignment.Units = list(units) - return unit_assignment - self.file.remove(unit_assignment) + unit_assignment = file.by_type("IfcUnitAssignment") + if not unit_assignment: + return + unit_assignment = unit_assignment[0] + units = set(unit_assignment.Units or []) + units = units - set(settings["units"]) + if units: + unit_assignment.Units = list(units) + return unit_assignment + file.remove(unit_assignment) diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py index e0caddbe3c..51e0db158b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_filling import add_filling +from .add_opening import add_opening +from .remove_filling import remove_filling +from .remove_opening import remove_opening diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py index a2867450f6..547178fff8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py @@ -20,103 +20,100 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, opening=None, element=None): - """Fill an opening with an element +def add_filling(file, opening=None, element=None) -> None: + """Fill an opening with an element - Physical elements may have openings in them. For example, a wall might - have an opening for a door. That opening is then filled by the door. - This indicates that when the door moves, the opening will move with it. - Or if the door is removed, then the opening may remain and need to be - filled. + Physical elements may have openings in them. For example, a wall might + have an opening for a door. That opening is then filled by the door. + This indicates that when the door moves, the opening will move with it. + Or if the door is removed, then the opening may remain and need to be + filled. - :param opening: The IfcOpeningElement to fill with the element. - :type opening: ifcopenshell.entity_instance - :param element: The IfcElement to be inserted into the opening. - :type element: ifcopenshell.entity_instance - :return: The new IfcRelFillsElement relationship - :rtype: ifcopenshell.entity_instance + :param opening: The IfcOpeningElement to fill with the element. + :type opening: ifcopenshell.entity_instance + :param element: The IfcElement to be inserted into the opening. + :type element: ifcopenshell.entity_instance + :return: The new IfcRelFillsElement relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall and opening. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Let's create an opening representation of a 950mm x 2100mm door. - # Notice how the thickness is greater than the wall thickness, this - # helps resolve floating point resolution errors in 3D. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.4) - ifcopenshell.api.run("geometry.assign_representation", model, - product=opening, representation=representation) + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, to create a nice overlap for the opening boolean. - matrix = np.identity(4) - matrix[:,3] = [1, -.1, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) - # The opening will now void the wall. - ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) - # Create a door - door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") - # Let's create a door representation of a 950mm x 2100mm door. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.05) - ifcopenshell.api.run("geometry.assign_representation", model, - product=door, representation=representation) + # Let's create a door representation of a 950mm x 2100mm door. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.05) + ifcopenshell.api.run("geometry.assign_representation", model, + product=door, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, which lines up with our opening. - matrix = np.identity(4) - matrix[:,3] = [1, .05, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, which lines up with our opening. + matrix = np.identity(4) + matrix[:,3] = [1, .05, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix) - # The door will now fill the opening. - ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) - """ - self.file = file - self.settings = {"opening": opening, "element": element} + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + """ + settings = {"opening": opening, "element": element} - def execute(self): - fills_voids = self.settings["element"].FillsVoids + fills_voids = settings["element"].FillsVoids - if fills_voids: - if fills_voids[0].RelatingOpeningElement == self.settings["opening"]: - return - history = fills_voids[0].OwnerHistory - self.file.remove(fills_voids[0]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if fills_voids: + if fills_voids[0].RelatingOpeningElement == settings["opening"]: + return + history = fills_voids[0].OwnerHistory + file.remove(fills_voids[0]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - self.file.create_entity( - "IfcRelFillsElement", - GlobalId=ifcopenshell.guid.new(), - RelatingOpeningElement=self.settings["opening"], - RelatedBuildingElement=self.settings["element"], - ) + file.create_entity( + "IfcRelFillsElement", + GlobalId=ifcopenshell.guid.new(), + RelatingOpeningElement=settings["opening"], + RelatedBuildingElement=settings["element"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py index 142eaedd87..d873ac2cd3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py @@ -22,118 +22,115 @@ import ifcopenshell.util.element import ifcopenshell.util.placement -class Usecase: - def __init__( - self, file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance - ): - """Create an opening in an element +def add_opening( + file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance +) -> ifcopenshell.entity_instance: + """Create an opening in an element - It is often necessary to cut out openings in elements like walls and - slabs to make space to insert doors, windows, and other services that go - through these penetrations. + It is often necessary to cut out openings in elements like walls and + slabs to make space to insert doors, windows, and other services that go + through these penetrations. - Whereas it is possible to simply draw the wall as a rectangle with a - hole in it for the opening, often these openings have specific meanings. - For example, an opening might be filled with a window, and so when the - window moves, the opening should move with it. Alternatively, the - opening itself might have fire or acoustic requirements, such that any - service or equipment passing through that space must also comply with - those requirements. For these types of semantic openings, you should - have a distinct opening element which voids your regular element. For - example, your wall will still be a rectangular prism with no hole in it, - and a separate opening element will have a box representing the extents - of the opening for a window. The opening element will automatically - perform a geometric boolean operation to cut out the wall's geometry. + Whereas it is possible to simply draw the wall as a rectangle with a + hole in it for the opening, often these openings have specific meanings. + For example, an opening might be filled with a window, and so when the + window moves, the opening should move with it. Alternatively, the + opening itself might have fire or acoustic requirements, such that any + service or equipment passing through that space must also comply with + those requirements. For these types of semantic openings, you should + have a distinct opening element which voids your regular element. For + example, your wall will still be a rectangular prism with no hole in it, + and a separate opening element will have a box representing the extents + of the opening for a window. The opening element will automatically + perform a geometric boolean operation to cut out the wall's geometry. - Whenever you have an opening in you project, you should determine - whether or not the opening is semantic (i.e. should be represented by a - distinct opening object) or non-semantic (i.e. should simply be - booleaned or be part of the shape of the object). + Whenever you have an opening in you project, you should determine + whether or not the opening is semantic (i.e. should be represented by a + distinct opening object) or non-semantic (i.e. should simply be + booleaned or be part of the shape of the object). - :param opening: The IfcOpeningElement to cut out the element. - :type opening: ifcopenshell.entity_instance - :param element: The IfcElement to insert the opening into. - :type element: ifcopenshell.entity_instance - :return: The new IfcRelVoidsElement relationship - :rtype: ifcopenshell.entity_instance + :param opening: The IfcOpeningElement to cut out the element. + :type opening: ifcopenshell.entity_instance + :param element: The IfcElement to insert the opening into. + :type element: ifcopenshell.entity_instance + :return: The new IfcRelVoidsElement relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall and opening. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Let's create an opening representation of a 950mm x 2100mm door. - # Notice how the thickness is greater than the wall thickness, this - # helps resolve floating point resolution errors in 3D. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.4) - ifcopenshell.api.run("geometry.assign_representation", model, - product=opening, representation=representation) + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, to create a nice overlap for the opening boolean. - matrix = np.identity(4) - matrix[:,3] = [1, -.1, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) - # The opening will now void the wall. - ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) - """ - self.file = file - self.settings = {"opening": opening, "element": element} + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + """ + settings = {"opening": opening, "element": element} - def execute(self) -> ifcopenshell.entity_instance: - voids_elements = self.settings["opening"].VoidsElements + voids_elements = settings["opening"].VoidsElements - if voids_elements: - if voids_elements[0].RelatingBuildingElement == self.settings["element"]: - return voids_elements[0] - history = voids_elements[0].OwnerHistory - self.file.remove(voids_elements[0]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if voids_elements: + if voids_elements[0].RelatingBuildingElement == settings["element"]: + return voids_elements[0] + history = voids_elements[0].OwnerHistory + file.remove(voids_elements[0]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - rel = self.file.create_entity( - "IfcRelVoidsElement", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatingBuildingElement": self.settings["element"], - "RelatedOpeningElement": self.settings["opening"], - } + rel = file.create_entity( + "IfcRelVoidsElement", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatingBuildingElement": settings["element"], + "RelatedOpeningElement": settings["opening"], + } + ) + + placement = getattr(settings["opening"], "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=settings["opening"], + matrix=ifcopenshell.util.placement.get_local_placement(settings["opening"].ObjectPlacement), + is_si=False, ) - placement = getattr(self.settings["opening"], "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=self.settings["opening"], - matrix=ifcopenshell.util.placement.get_local_placement(self.settings["opening"].ObjectPlacement), - is_si=False, - ) - - return rel + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py index 6d5ab79752..b4c3188672 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py @@ -20,47 +20,44 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, element=None): - """Remove a filling relationship +def remove_filling(file, element=None) -> None: + """Remove a filling relationship - If an element is filling an opening, this removes the relationship such - that the opening and element both still exist, but the element no longer - fills the opening. + If an element is filling an opening, this removes the relationship such + that the opening and element both still exist, but the element no longer + fills the opening. - :param element: The element filling an opening. - :type element: ifcopenshell.entity_instance - :return: None - :rtype: None + :param element: The element filling an opening. + :type element: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Create a door - door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") - # The door will now fill the opening. - ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) - # Not anymore! - ifcopenshell.api.run("void.remove_filling", model, element=door) - """ - self.file = file - self.settings = {"element": element} + # Not anymore! + ifcopenshell.api.run("void.remove_filling", model, element=door) + """ + settings = {"element": element} - def execute(self): - for rel in self.file.by_type("IfcRelFillsElement"): - if rel.RelatedBuildingElement == self.settings["element"]: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - break + for rel in file.by_type("IfcRelFillsElement"): + if rel.RelatedBuildingElement == settings["element"]: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + break diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py index 5ffba93e25..58b7782333 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py @@ -20,44 +20,41 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance): - """Remove an opening +def remove_opening(file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance) -> None: + """Remove an opening - Fillings are retained as orphans. Voided elements remain. Openings - cannot exist by themselves, so not only is the opening relationship - removed, the opening is also removed. + Fillings are retained as orphans. Voided elements remain. Openings + cannot exist by themselves, so not only is the opening relationship + removed, the opening is also removed. - :param opening: The IfcOpeningElement to remove. - :type opening: ifcopenshell.entity_instance - :return: None - :rtype: None + :param opening: The IfcOpeningElement to remove. + :type opening: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create an oprhaned opening. Note that an orphaned opening is - # invalid, as an opening can only exist when voiding another - # element. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an oprhaned opening. Note that an orphaned opening is + # invalid, as an opening can only exist when voiding another + # element. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Remove it. This brings us back to a valid model. - ifcopenshell.api.run("void.remove_opening", model, opening=opening) - """ - self.file = file - self.settings = {"opening": opening} + # Remove it. This brings us back to a valid model. + ifcopenshell.api.run("void.remove_opening", model, opening=opening) + """ + settings = {"opening": opening} - def execute(self) -> None: - for rel in self.settings["opening"].VoidsElements: + for rel in settings["opening"].VoidsElements: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + if settings["opening"].is_a("IfcOpeningElement"): + for rel in settings["opening"].HasFillings: history = rel.OwnerHistory - self.file.remove(rel) + file.remove(rel) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - if self.settings["opening"].is_a("IfcOpeningElement"): - for rel in self.settings["opening"].HasFillings: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run("root.remove_product", self.file, product=self.settings["opening"]) + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run("root.remove_product", file, product=settings["opening"]) From ab5ea4c853e947f2c2b6678fee6c92c799b675b5 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 6 May 2024 14:43:37 +1000 Subject: [PATCH 36/62] Implement listener wrapper for new API functions. See #2693. --- .../ifcopenshell/api/__init__.py | 82 +++++++------------ 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 805811744e..7666150915 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -21,6 +21,7 @@ import json import numpy import pkgutil +import inspect import importlib import ifcopenshell import ifcopenshell.api @@ -247,7 +248,6 @@ def remove_all_listeners(): def extract_docs(module, usecase): import typing - import inspect import collections results = [] @@ -295,60 +295,31 @@ def extract_docs(module, usecase): return node_data -def _wrap_api(init_globals, file, package): - """API endpoints are implemented as Usecase classes. This wraps the classes as functions. +def wrap_usecase(usecase_path, usecase): + """Wraps an API function in pre/post listeners.""" - Calling classes is syntactically awkward. For example, - ifcopenshell.api.root.create_entity.Usecase(f).execute(). - It is more elegant to call it using ifcopenshell.api.root.create_entity(f). + def wrapper(*args, should_run_listeners: bool = True, **settings): + ifc_file = args[0] if args else None + if should_run_listeners: + for listener in pre_listeners.get(usecase_path, {}).values(): + listener(usecase_path, ifc_file, settings) - Calling _wrap_api from an API package's __init__.py will generate these - wrapper functions at runtime. - """ - import pkgutil - import importlib - import inspect - from pathlib import Path - - def _create_function(module_name, Usecase): - """Create a function that wraps the Usecase class's execute method.""" - usecase_path = ".".join(Usecase.__module__.split(".")[-2:]) - - def wrapper(*args, should_run_listeners: bool = True, **settings): - ifc_file = args[0] if args else None - if should_run_listeners: - for listener in pre_listeners.get(usecase_path, {}).values(): - listener(usecase_path, ifc_file, settings) - - try: - usecase = Usecase(*args, **settings) - except TypeError as e: - msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(Usecase.__init__)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation." - raise TypeError(msg) from e - - result = usecase.execute() - - if should_run_listeners: - for listener in post_listeners.get(usecase_path, {}).values(): - listener(usecase_path, ifc_file, settings) - - return result - - wrapper.__signature__ = inspect.signature(Usecase.__init__) - wrapper.__doc__ = Usecase.__init__.__doc__ - wrapper.__name__ = module_name - return wrapper - - for finder, name, ispkg in pkgutil.iter_modules([Path(file).parent]): try: - module = importlib.import_module(f".{name}", package) - except ModuleNotFoundError as e: - print(f"Note: API not available due to missing dependencies: {package}.{name} - {e}") - continue - usecase_cls = getattr(module, "Usecase", None) - if usecase_cls: - func = _create_function(name, usecase_cls) - init_globals[name] = func + result = usecase(*args, **settings) + except TypeError as e: + msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(Usecase.__init__)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation." + raise TypeError(msg) from e + + if should_run_listeners: + for listener in post_listeners.get(usecase_path, {}).values(): + listener(usecase_path, ifc_file, settings) + + return result + + wrapper.__signature__ = inspect.signature(usecase) + wrapper.__doc__ = usecase.__doc__ + wrapper.__name__ = usecase_path + return wrapper # Expose all submodules. This means that the user can just type `import ifcopenshell.api`. @@ -357,5 +328,8 @@ for loader, module_name, is_pkg in pkgutil.iter_modules(__path__, __name__ + "." # Check if it's a direct child (only one level deep) if module_name.count(".") == __name__.count(".") + 1: - # Generate wrapper functions for each usecase - _wrap_api(vars(module), module.__file__, module.__name__) + for usecase_name in vars(module): + usecase = getattr(module, usecase_name) + if callable(usecase): + usecase_path = f"{module_name.split('.')[-1]}.{usecase_name}" + setattr(module, usecase_name, wrap_usecase(usecase_path, usecase)) From b718bd19bd1346299266f75225b69d79e21692f1 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Mon, 6 May 2024 09:05:56 +0100 Subject: [PATCH 37/62] fix TypeError File "/usr/lib64/python3.12/site-packages/ifcopenshell/api/project/assign_declaration.py", line 126, in execute related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~ TypeError: unsupported operand type(s) for -: 'set' and 'list' --- .../ifcopenshell/api/project/assign_declaration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py index 776a26b8f5..e1be64ba7f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -119,9 +119,9 @@ def assign_declaration( return None for has_context in previous_declares_rels: - related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts + related_definitions = set(has_context.RelatedDefinitions) - set(objects_with_contexts) if related_definitions: - has_context.RelatedDefinitions = related_definitions + has_context.RelatedDefinitions = list(related_definitions) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": has_context}) else: history = has_context.OwnerHistory From 6c313dd25c1b58e1af8c1e052667582dcb70dda5 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 6 May 2024 18:38:58 +1000 Subject: [PATCH 38/62] Fix for static analysis of ifcopenshell.api submodules. See #2693. --- .../ifcopenshell/api/__init__.py | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 7666150915..dcfbd1821c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -323,13 +323,49 @@ def wrap_usecase(usecase_path, usecase): # Expose all submodules. This means that the user can just type `import ifcopenshell.api`. -for loader, module_name, is_pkg in pkgutil.iter_modules(__path__, __name__ + "."): - module = importlib.import_module(module_name) +import ifcopenshell.api.aggregate as aggregate +import ifcopenshell.api.attribute as attribute +import ifcopenshell.api.boundary as boundary +import ifcopenshell.api.classification as classification +import ifcopenshell.api.constraint as constraint +import ifcopenshell.api.context as context +import ifcopenshell.api.control as control +import ifcopenshell.api.cost as cost +import ifcopenshell.api.document as document +import ifcopenshell.api.drawing as drawing +import ifcopenshell.api.geometry as geometry +import ifcopenshell.api.georeference as georeference +import ifcopenshell.api.grid as grid +import ifcopenshell.api.group as group +import ifcopenshell.api.layer as layer +import ifcopenshell.api.library as library +import ifcopenshell.api.material as material +import ifcopenshell.api.nest as nest +import ifcopenshell.api.owner as owner +import ifcopenshell.api.profile as profile +import ifcopenshell.api.project as project +import ifcopenshell.api.pset as pset +import ifcopenshell.api.pset_template as pset_template +import ifcopenshell.api.resource as resource +import ifcopenshell.api.root as root +import ifcopenshell.api.sequence as sequence +import ifcopenshell.api.spatial as spatial +import ifcopenshell.api.structural as structural +import ifcopenshell.api.style as style +import ifcopenshell.api.system as system +import ifcopenshell.api.type as type # Whoohoo! +import ifcopenshell.api.unit as unit +import ifcopenshell.api.void as void +# Wrap all submodule usecases with listeners. +# This for loop also conveniently ensures that the above imports are comprehensive. +for loader, module_name, is_pkg in pkgutil.iter_modules(__path__, __name__ + "."): # Check if it's a direct child (only one level deep) if module_name.count(".") == __name__.count(".") + 1: + module_name = module_name.split(".")[-1] + module = globals()[module_name] for usecase_name in vars(module): usecase = getattr(module, usecase_name) if callable(usecase): - usecase_path = f"{module_name.split('.')[-1]}.{usecase_name}" + usecase_path = f"{module_name}.{usecase_name}" setattr(module, usecase_name, wrap_usecase(usecase_path, usecase)) From 8f7f6223de6dbe6287652fed12b7a8a46db2ca4f Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 15:03:27 +0500 Subject: [PATCH 39/62] unlink pasted blender objects if there is no active ifc file #4619 --- .../blenderbim/bim/module/geometry/operator.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 9dd2b04ea1..42ee81af7a 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -1446,14 +1446,13 @@ class OverridePasteBuffer(bpy.types.Operator): def execute(self, context): bpy.ops.view3d.pastebuffer() - if IfcStore.get_file(): - for obj in context.selected_objects: - # Pasted objects may come from another Blender session, or even - # from the same session where the original object has since - # been deleted. As the source element may not exist, paste will - # always unlink the element. If you want to duplicate an - # element, use the duplicate commands. - tool.Root.unlink_object(obj) + for obj in context.selected_objects: + # Pasted objects may come from another Blender session, or even + # from the same session where the original object has since + # been deleted. As the source element may not exist, paste will + # always unlink the element. If you want to duplicate an + # element, use the duplicate commands. + tool.Root.unlink_object(obj) return {"FINISHED"} From b3e7975b83a19d5778475a38061833df5061037b Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 6 May 2024 13:18:05 +0200 Subject: [PATCH 40/62] Consider material profile set in single material determination --- src/ifcgeom/IfcGeom.cpp | 49 +++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/ifcgeom/IfcGeom.cpp b/src/ifcgeom/IfcGeom.cpp index ebbf24b89a..8b1bfda107 100644 --- a/src/ifcgeom/IfcGeom.cpp +++ b/src/ifcgeom/IfcGeom.cpp @@ -549,6 +549,36 @@ IfcSchema::IfcRelVoidsElement::list::ptr IfcGeom::Kernel::find_openings(IfcSchem return openings; } +namespace { + template + IfcSchema::IfcMaterial* get_single_from_aggregate(bool take_first_regardless_of_size, const T& agg) { + if (take_first_regardless_of_size ? agg->size() >= 1 : agg->size() == 1) { + auto* layer_or_profile = *agg->begin(); + if (layer_or_profile->Material()) { + return layer_or_profile->Material(); + } + } + return nullptr; + } +#ifdef SCHEMA_HAS_IfcMaterialProfileSet + IfcSchema::IfcMaterial* get_single_from_set(bool take_first_regardless_of_size, IfcSchema::IfcMaterialProfileSet* profileset) { + return get_single_from_aggregate(take_first_regardless_of_size, profileset->MaterialProfiles()); + } +#endif + IfcSchema::IfcMaterial* get_single_from_set(bool take_first_regardless_of_size, IfcSchema::IfcMaterialLayerSet* profileset) { + return get_single_from_aggregate(take_first_regardless_of_size, profileset->MaterialLayers()); + } + + IfcSchema::IfcMaterial* get_single_from_usage(bool take_first_regardless_of_size, IfcSchema::IfcMaterialLayerSetUsage* usage) { + return get_single_from_set(take_first_regardless_of_size, usage->ForLayerSet()); + } +#ifdef SCHEMA_HAS_IfcMaterialProfileSet + IfcSchema::IfcMaterial* get_single_from_usage(bool take_first_regardless_of_size, IfcSchema::IfcMaterialProfileSetUsage* usage) { + return get_single_from_set(take_first_regardless_of_size, usage->ForProfileSet()); + } +#endif +} + const IfcSchema::IfcMaterial* IfcGeom::Kernel::get_single_material_association(const IfcSchema::IfcProduct* product) { IfcSchema::IfcMaterial* single_material = 0; IfcSchema::IfcRelAssociatesMaterial::list::ptr associated_materials = product->HasAssociations()->as(); @@ -566,14 +596,19 @@ const IfcSchema::IfcMaterial* IfcGeom::Kernel::get_single_material_association(c single_material = associated_material->as(); // NB: IfcMaterialLayerSets are also considered, regardless of --enable-layerset-slicing. Picking // the first material (in accordance with other viewers) when layerset-slicing is disabled. - if (!single_material && associated_material->as()) { - IfcSchema::IfcMaterialLayerSet* layerset = associated_material->as()->ForLayerSet(); - if (getValue(GV_LAYERSET_FIRST) > 0.0 ? layerset->MaterialLayers()->size() >= 1 : layerset->MaterialLayers()->size() == 1) { - IfcSchema::IfcMaterialLayer* layer = (*layerset->MaterialLayers()->begin()); - if (layer->Material()) { - single_material = layer->Material(); - } + if (!single_material) { + if (auto* m = associated_material->as()) { + single_material = get_single_from_usage(getValue(GV_LAYERSET_FIRST) > 0.0, m); + } else if (auto* m = associated_material->as()) { + single_material = get_single_from_set(getValue(GV_LAYERSET_FIRST) > 0.0, m); } +#ifdef SCHEMA_HAS_IfcMaterialProfileSet + else if (auto* m = associated_material->as()) { + single_material = get_single_from_usage(getValue(GV_LAYERSET_FIRST) > 0.0, m); + } else if (auto* m = associated_material->as()) { + single_material = get_single_from_set(getValue(GV_LAYERSET_FIRST) > 0.0, m); + } +#endif } } } From 30b870b98db9f6da56d9fcc4c03cfc29206b3c6d Mon Sep 17 00:00:00 2001 From: Gorgious56 Date: Mon, 6 May 2024 13:20:33 +0200 Subject: [PATCH 41/62] Fix #4617 : Switching representation in edit mode is no longer possible --- .../bim/module/geometry/operator.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 42ee81af7a..312ceed51a 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -215,6 +215,13 @@ class SwitchRepresentation(bpy.types.Operator, Operator): disable_opening_subtractions: bpy.props.BoolProperty() should_switch_all_meshes: bpy.props.BoolProperty() + @classmethod + def poll(cls, context): + if context.active_object.mode == "OBJECT": + return True + cls.poll_message_set("Only available in OBJECT mode - Press TAB in the viewport") + return False + def _execute(self, context): target_representation = tool.Ifc.get().by_id(self.ifc_definition_id) target = target_representation.ContextOfItems @@ -223,6 +230,8 @@ class SwitchRepresentation(bpy.types.Operator, Operator): element = tool.Ifc.get_entity(obj) if not element: continue + if not obj.mode == "OBJECT": + continue if obj == context.active_object: representation = target_representation else: @@ -909,7 +918,7 @@ class OverrideDuplicateMove(bpy.types.Operator): if pset: pset = tool.Ifc.get().by_id(pset["id"]) ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset) - + if new[0].is_a("IfcElementAssembly"): linked_aggregate_group = [ r.RelatingGroup @@ -987,7 +996,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): else: index = add_linked_aggregate_pset(part, index) # index += 1 - + obj = tool.Ifc.get_object(part) obj.select_set(True) @@ -1021,9 +1030,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): return linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name) - ifcopenshell.api.run( - "group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group - ) + ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group) def custom_incremental_naming_for_element_assembly(old_to_new): for new in old_to_new.values(): @@ -1047,10 +1054,10 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): if re.findall(pattern2, new_obj.name): split_name = new_obj.name.split(".") new_obj.name = split_name[0] + "_" + number - + def get_max_index(parts): psets = [ifcopenshell.util.element.get_pset(p, "BBIM_Linked_Aggregate") for p in parts] - index = [i['Index'] for i in psets if i] + index = [i["Index"] for i in psets if i] if len(index) > 0: index = max(index) return index @@ -1064,14 +1071,14 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): new_pset = ifcopenshell.api.run( "pset.add_pset", tool.Ifc.get(), product=new[0], name=self.pset_name ) - + ifcopenshell.api.run( "pset.edit_pset", tool.Ifc.get(), pset=new_pset, properties={"Index": pset["Index"]}, ) - + if new[0].is_a("IfcElementAssembly"): linked_aggregate_group = [ r.RelatingGroup @@ -1080,7 +1087,6 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name ] tool.Ifc.run("group.assign_group", group=linked_aggregate_group[0], products=new) - if len(context.selected_objects) != 1: return {"FINISHED"} @@ -1101,7 +1107,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): select_objects_and_add_data(selected_element) old_to_new = OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True) - + tool.Root.recreate_aggregate(old_to_new) copy_linked_aggregate_data(old_to_new) @@ -1250,9 +1256,9 @@ class RefreshLinkedAggregate(bpy.types.Operator): selected_matrix = selected_obj.matrix_world object_duplicate = tool.Ifc.get_object(element) duplicate_matrix = object_duplicate.matrix_world.decompose() - + return selected_matrix, duplicate_matrix - + def set_new_matrix(selected_matrix, duplicate_matrix, old_to_new): for old, new in old_to_new.items(): new_obj = tool.Ifc.get_object(new[0]) @@ -1260,7 +1266,6 @@ class RefreshLinkedAggregate(bpy.types.Operator): matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world new_obj_matrix = new_base_matrix @ matrix_diff new_obj.matrix_world = new_obj_matrix - active_element = tool.Ifc.get_entity(context.active_object) if not active_element: From 3b9008610990d57ba155a154a7a147ce8011b05b Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 16:57:02 +0500 Subject: [PATCH 42/62] Fix loading search queries with exclusion #4609 It was failing to load queries like "!IfcWall" or "!2Mg7PHubX0xxOfF0DdA9Wd" --- src/blenderbim/blenderbim/tool/search.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/tool/search.py b/src/blenderbim/blenderbim/tool/search.py index cbad2ac02e..3d77e1653f 100644 --- a/src/blenderbim/blenderbim/tool/search.py +++ b/src/blenderbim/blenderbim/tool/search.py @@ -160,10 +160,16 @@ class ImportFilterQueryTransformer(lark.Transformer): return args[0] def instance(self, args): - return {"type": "instance", "value": " ".join([a.children[0].value for a in args])} + if args[0].data == "not": + return {"type": "instance", "value": "!" + args[1].children[0].value} + else: + return {"type": "instance", "value": args[0].children[0].value} def entity(self, args): - return {"type": "entity", "value": " ".join([a.children[0].value for a in args])} + if args[0].data == "not": + return {"type": "entity", "value": "!" + args[1].children[0].value} + else: + return {"type": "entity", "value": args[0].children[0].value} def attribute(self, args): name, comparison, value = args From 31d322a28c435173e6b451ee973280bde62de187 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 17:29:30 +0500 Subject: [PATCH 43/62] descriptions for operators removing classifications/contexts #4614 --- .../blenderbim/bim/module/classification/operator.py | 4 ++++ src/blenderbim/blenderbim/bim/module/context/operator.py | 4 ++++ .../ifcopenshell/api/classification/remove_classification.py | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/classification/operator.py b/src/blenderbim/blenderbim/bim/module/classification/operator.py index 623aa59770..7119edae3f 100644 --- a/src/blenderbim/blenderbim/bim/module/classification/operator.py +++ b/src/blenderbim/blenderbim/bim/module/classification/operator.py @@ -209,6 +209,10 @@ class DisableEditingClassification(bpy.types.Operator): class RemoveClassification(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_classification" bl_label = "Remove Classification" + bl_description = ( + "The classification and all of its relationships, children references, " + "and relationships between objects and child references will be completely removed from a project" + ) bl_options = {"REGISTER", "UNDO"} classification: bpy.props.IntProperty() diff --git a/src/blenderbim/blenderbim/bim/module/context/operator.py b/src/blenderbim/blenderbim/bim/module/context/operator.py index fa545168aa..bba6561848 100644 --- a/src/blenderbim/blenderbim/bim/module/context/operator.py +++ b/src/blenderbim/blenderbim/bim/module/context/operator.py @@ -53,6 +53,10 @@ class AddContext(bpy.types.Operator, Operator): class RemoveContext(bpy.types.Operator, Operator): bl_idname = "bim.remove_context" bl_label = "Remove Context" + bl_description = ( + "Remove representation context. Any representation geometry that is assigned to the context is also removed. " + "If a context is removed, then any subcontexts are also removed" + ) bl_options = {"REGISTER", "UNDO"} context: bpy.props.IntProperty() diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py index 42ec050d61..72764006c0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -24,7 +24,7 @@ def remove_classification(file, classification=None) -> None: """Removes an IfcClassification from the project and all references The classification and all of its relationships, children references, - and relationships between objectse and child references are completely + and relationships between objects and child references are completely removed from a project. :param classification: The IfcClassification entity you want to remove From 54a3730c2024e431b08998de73889ef484b63d24 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Mon, 6 May 2024 13:55:20 +0100 Subject: [PATCH 44/62] fix d11ec67 mathutils dependency regression mathutils is a blender module, wrap imports in try/except --- .../ifcopenshell/api/geometry/__init__.py | 15 ++++++++++++--- .../ifcopenshell/api/grid/__init__.py | 5 ++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py index 1caaa312ba..0aa8756ea0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py @@ -18,11 +18,17 @@ from .add_axis_representation import add_axis_representation from .add_boolean import add_boolean -from .add_door_representation import add_door_representation +try: + from .add_door_representation import add_door_representation +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: geometry.add_door_representation - {e}") from .add_footprint_representation import add_footprint_representation from .add_mesh_representation import add_mesh_representation from .add_profile_representation import add_profile_representation -from .add_railing_representation import add_railing_representation +try: + from .add_railing_representation import add_railing_representation +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: geometry.add_railing_representation - {e}") try: from .add_representation import add_representation @@ -30,7 +36,10 @@ except ModuleNotFoundError as e: print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}") from .add_slab_representation import add_slab_representation from .add_wall_representation import add_wall_representation -from .add_window_representation import add_window_representation +try: + from .add_window_representation import add_window_representation +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: geometry.add_window_representation - {e}") from .assign_representation import assign_representation from .connect_element import connect_element from .connect_path import connect_path diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py index c66e86a668..9991a7bc06 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py @@ -16,6 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from .create_axis_curve import create_axis_curve +try: + from .create_axis_curve import create_axis_curve +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: grid.create_axis_curve - {e}") from .create_grid_axis import create_grid_axis from .remove_grid_axis import remove_grid_axis From a9d785c28f0ef2bdd105a9360b50a1cf5b7e0c19 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 17:48:25 +0500 Subject: [PATCH 45/62] typing --- src/ifccsv/ifccsv.py | 32 ++++++++++------ .../classification/remove_classification.py | 2 +- .../api/context/remove_context.py | 2 +- .../ifcopenshell/api/pset/edit_pset.py | 38 ++++++++++++------- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/ifccsv/ifccsv.py b/src/ifccsv/ifccsv.py index bcb7f4855f..f89f152a1e 100755 --- a/src/ifccsv/ifccsv.py +++ b/src/ifccsv/ifccsv.py @@ -61,20 +61,20 @@ class IfcCsv: def export( self, - ifc_file, - elements, + ifc_file: ifcopenshell.file, + elements: ifcopenshell.entity_instance, attributes, headers=None, output=None, format=None, - should_preserve_existing=False, - include_global_id=True, - delimiter=",", - null="-", - empty="", - bool_true="YES", - bool_false="NO", - concat=", ", + should_preserve_existing: bool = False, + include_global_id: bool = True, + delimiter: str = ",", + null: str = "-", + empty: str = "", + bool_true: str = "YES", + bool_false: str = "NO", + concat: str = ", ", sort=None, groups=None, summaries=None, @@ -382,8 +382,16 @@ class IfcCsv: return ["{}.{}".format(pset_qto_name, n) for n in results] def Import( - self, ifc_file, table, attributes=None, delimiter=",", null="-", empty="", bool_true="YES", bool_false="NO" - ): + self, + ifc_file: ifcopenshell.file, + table: str, + attributes: Optional[list[Union[str, None]]] = None, + delimiter: str = ",", + null: str = "-", + empty: str = "", + bool_true: str = "YES", + bool_false: str = "NO", + ) -> None: ext = table.split(".")[-1].lower() if ext == "csv": diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py index 72764006c0..21e02d7b86 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -20,7 +20,7 @@ import ifcopenshell import ifcopenshell.util.element -def remove_classification(file, classification=None) -> None: +def remove_classification(file: ifcopenshell.entity_instance, classification: ifcopenshell.entity_instance) -> None: """Removes an IfcClassification from the project and all references The classification and all of its relationships, children references, diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py index 9ac30483cf..94547b675e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py @@ -19,7 +19,7 @@ import ifcopenshell -def remove_context(file, context=None) -> None: +def remove_context(file: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance) -> None: """Removes an IfcGeometricRepresentationContext Any representation geometry that is assigned to the context is also diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index c4955e1605..be3ed6c00a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -18,9 +18,17 @@ import ifcopenshell import ifcopenshell.util.pset +from typing import Optional, Any, Union -def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, should_purge=False) -> None: +def edit_pset( + file: ifcopenshell.entity_instance, + pset: ifcopenshell.entity_instance, + name: Optional[str] = None, + properties: Optional[dict[str, Any]] = None, + pset_template: Optional[ifcopenshell.entity_instance] = None, + should_purge: bool = False, +) -> None: """Edits a property set and its properties At its simplest usage, this may be used to edit the name of a property @@ -68,7 +76,7 @@ def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, s :param pset_template: If a property set template is provided, this will be used to determine data types. If no user-defined template is provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance + :type pset_template: ifcopenshell.entity_instance, optional :param should_purge: If left as False, properties set to None will be left as None but not removed. If set to true, properties set to None will actually be removed. @@ -158,18 +166,18 @@ def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, s class Usecase: - def execute(self): + def execute(self) -> None: self.update_pset_name() self.load_pset_template() existing_props = self.update_existing_properties() new_props = self.add_new_properties() self.assign_new_properties(existing_props + new_props) - def update_pset_name(self): + def update_pset_name(self) -> None: if self.settings["name"]: self.settings["pset"].Name = self.settings["name"] - def load_pset_template(self): + def load_pset_template(self) -> None: if self.settings["pset_template"]: self.pset_template = self.settings["pset_template"] else: @@ -177,13 +185,13 @@ class Usecase: self.psetqto = ifcopenshell.util.pset.get_template(self.file.schema) self.pset_template = self.psetqto.get_by_name(self.settings["pset"].Name) - def _should_update_prop(self, prop) -> bool: + def _should_update_prop(self, prop: ifcopenshell.entity_instance) -> bool: """ Checks if the given property should be changed """ return prop.Name in self.settings["properties"] - def _try_purge(self, prop) -> bool: + def _try_purge(self, prop: ifcopenshell.entity_instance) -> bool: """ Tries to remove the property if successful, returns True, otherwise False @@ -200,7 +208,7 @@ class Usecase: # For example - IfcPropertyEnumeratedValue to # IfcPropertySingleValue. Or maybe the user should # just delete the property first? - vulevukusej - def update_existing_properties(self): + def update_existing_properties(self) -> list[ifcopenshell.entity_instance]: existing_props = [] for prop in self.get_properties(): if not self._should_update_prop(prop): @@ -222,7 +230,9 @@ class Usecase: raise NotImplementedError(f"Updating '{prop.is_a()}' properties is not supported yet") return existing_props - def update_existing_prop_enum(self, prop): + def update_existing_prop_enum( + self, prop: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: """ NOTE: Assumes the prop exists """ @@ -255,7 +265,9 @@ class Usecase: del self.settings["properties"][prop.Name] return prop - def update_existing_prop_single_value(self, prop): + def update_existing_prop_single_value( + self, prop: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: """ NOTE: Assumes the prop exists """ @@ -278,7 +290,7 @@ class Usecase: del self.settings["properties"][prop.Name] return prop - def add_new_properties(self): + def add_new_properties(self) -> list[ifcopenshell.entity_instance]: properties = [] for name, value in self.settings["properties"].items(): if value is None and self.settings["should_purge"]: @@ -358,13 +370,13 @@ class Usecase: properties.append(self.file.create_entity("IfcPropertySingleValue", **args)) return properties - def assign_new_properties(self, props): + def assign_new_properties(self, props: ifcopenshell.entity_instance) -> None: if hasattr(self.settings["pset"], "HasProperties"): self.settings["pset"].HasProperties = props elif hasattr(self.settings["pset"], "Properties"): self.settings["pset"].Properties = props - def get_properties(self): + def get_properties(self) -> list[ifcopenshell.entity_instance]: """ Returns list of existing properties """ From dcc52fc4e49c49eb0d72e4159c721cea91193df6 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 18:09:32 +0500 Subject: [PATCH 46/62] pset.edit_pset not to fail silently on invalid enum values #4608 --- src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index be3ed6c00a..01d5d9b2ea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -260,6 +260,11 @@ class Usecase: prop.EnumerationReference.EnumerationValues = value.EnumerationReference.EnumerationValues prop.EnumerationValues = value.EnumerationValues + else: + raise ValueError( + f'Value "{self.settings["properties"][prop.Name]}" is not a valid value for enum property {prop.Name}.' + ) + if unit: prop.Unit = unit del self.settings["properties"][prop.Name] From 28d205b4a3cd77052df9f867f33ee6ecc23b4ba9 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 6 May 2024 18:54:49 +0500 Subject: [PATCH 47/62] show info message on saving/loading csv from bbim --- src/blenderbim/blenderbim/bim/module/csv/operator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/csv/operator.py b/src/blenderbim/blenderbim/bim/module/csv/operator.py index 34beeb5e27..b62d9b93a1 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/operator.py +++ b/src/blenderbim/blenderbim/bim/module/csv/operator.py @@ -246,6 +246,7 @@ class ExportIfcCsv(bpy.types.Operator): if props.format != "csv" and props.should_generate_svg: schedule_creator = scheduler.Scheduler() schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg")) + self.report({"INFO"}, f"Data is exported to {props.format.upper()}.") return {"FINISHED"} @@ -285,6 +286,7 @@ class ImportIfcCsv(bpy.types.Operator): if not props.should_load_from_memory: ifc_file.write(props.csv_ifc_file) refresh_ui_data() + self.report({"INFO"}, "Data is imported to IFC.") return {"FINISHED"} From 7e13ed746ee3b4bb8b622697e6cf13a60f170438 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 10:32:02 +1000 Subject: [PATCH 48/62] Don't rely on util for basic ifcopenshell module capabilities. Keep util as an optional module for users to load. --- .../ifcopenshell/__init__.py | 40 ++++++++++++++++--- src/ifcopenshell-python/ifcopenshell/file.py | 22 +++++----- src/ifcopenshell-python/ifcopenshell/sql.py | 18 ++++++--- .../ifcopenshell/util/file.py | 29 -------------- 4 files changed, 60 insertions(+), 49 deletions(-) delete mode 100644 src/ifcopenshell-python/ifcopenshell/util/file.py diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 3f4a6acaa2..7c936f7b06 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -36,8 +36,8 @@ from __future__ import print_function import os import sys -import tempfile import zipfile +import tempfile from pathlib import Path from typing import Optional @@ -73,9 +73,11 @@ from . import guid from .file import file from .entity_instance import entity_instance, register_schema_attributes from .sql import sqlite, sqlite_entity + try: from .stream import stream, stream_entity -except: pass +except: + pass READ_ERROR = ifcopenshell_wrapper.file_open_status.READ_ERROR NO_HEADER = ifcopenshell_wrapper.file_open_status.NO_HEADER @@ -84,11 +86,13 @@ UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA class Error(Exception): """Error used when a generic problem occurs""" + pass class SchemaError(Error): """Error used when an IFC schema related problem occurs""" + pass @@ -114,7 +118,7 @@ def open(path: "os.PathLike | str", format: str = None, should_stream: bool = Fa """ path = Path(path) if format is None: - format = ifcopenshell.util.file.guess_format(path) + format = guess_format(path) if format == ".ifcXML": f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute())) if f: @@ -141,8 +145,7 @@ def open(path: "os.PathLike | str", format: str = None, should_stream: bool = Fa NO_HEADER: (Error, "Unable to parse IFC SPF header"), UNSUPPORTED_SCHEMA: ( SchemaError, - "Unsupported schema: %s" - % ",".join(f.header.file_schema.schema_identifiers), + "Unsupported schema: %s" % ",".join(f.header.file_schema.schema_identifiers), ), }[f.good().value()] raise exc(msg) @@ -226,4 +229,31 @@ def schema_by_name( return ifcopenshell_wrapper.schema_by_name(schema) +def guess_format(path: Path) -> Union[str | None]: + """Try to guess format using file extension + + IFCs may be serialised as different formats. The most common is a ``.ifc`` + file, which is plaintext and stores data using the STEP Physical File + format. IFC can also be stored as a Zipfile, XML, JSON, or SQL. + + This will return the canonical form of the format. For example, if a path + has the extension of .xml or .ifcxml (case insensitive), it will return + .ifcXML. + + :return: Either .ifc, .ifcZIP, .ifcXML, .ifcJSON, .ifcSQLite, or None. + """ + suffix = path.suffix.lower() + if suffix == ".ifc": + return ".ifc" + elif suffix in (".ifczip", ".zip"): + return ".ifcZIP" + elif suffix in (".ifcxml", ".xml"): + return ".ifcXML" + elif suffix in (".ifcjson", ".json"): + return ".ifcJSON" + elif suffix in (".ifcsqlite", ".sqlite", ".db"): + return ".ifcSQLite" + return None + + from .main import * diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 3ca1854db9..40986a3735 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -27,11 +27,10 @@ import re import numbers import zipfile import functools +import ifcopenshell from pathlib import Path from typing import Optional, Any -import ifcopenshell.util.element -import ifcopenshell.util.file from . import ifcopenshell_wrapper from .entity_instance import entity_instance @@ -120,11 +119,19 @@ class Transaction: for inverse in self.file.get_inverse(element): inverse_references = [] for i, attribute in enumerate(inverse): - if ifcopenshell.util.element.has_element_reference(attribute, element): + if self.has_element_reference(attribute, element): inverse_references.append((i, self.serialise_value(inverse, attribute))) inverses[inverse.id()] = inverse_references return inverses + def has_element_reference(self, value: Any, element: ifcopenshell.entity_instance) -> bool: + if isinstance(value, (tuple, list)): + for v in value: + if self.has_element_reference(v, element): + return True + return False + return value == element + def rollback(self): for operation in self.operations[::-1]: if operation["action"] == "create": @@ -376,14 +383,11 @@ class file(object): match = re.match(reg, self.wrapped_data.schema) version_tuple = tuple( map( - lambda pp: int(pp[1][len(pp[0]):]) if pp[1] else None, + lambda pp: int(pp[1][len(pp[0]) :]) if pp[1] else None, ((p, match.group(p)) for p in prefixes), ) ) - return "".join( - "".join(map(str, t)) if t[1] else "" - for t in zip(prefixes, version_tuple[0:2]) - ) + return "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, version_tuple[0:2])) elif attr == "schema_identifier": return self.wrapped_data.schema elif attr == "schema_version": @@ -576,7 +580,7 @@ class file(object): path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) if format == None: - format = ifcopenshell.util.file.guess_format(path) + format = ifcopenshell.guess_format(path) if format == ".ifcXML": serializer = ifcopenshell_wrapper.XmlSerializer(self, str(path)) serializer.finalize() diff --git a/src/ifcopenshell-python/ifcopenshell/sql.py b/src/ifcopenshell-python/ifcopenshell/sql.py index 546b2b4101..343a09561a 100644 --- a/src/ifcopenshell-python/ifcopenshell/sql.py +++ b/src/ifcopenshell-python/ifcopenshell/sql.py @@ -2,7 +2,6 @@ try: import re import json - import ifcopenshell.util.schema from .file import file from . import ifcopenshell_wrapper from .entity_instance import entity_instance @@ -56,6 +55,8 @@ class sqlite(file): self.preprocess_schema() def preprocess_schema(self): + import ifcopenshell.util.schema + self.ifc_class_subtypes = {} self.ifc_class_attributes = {} self.ifc_class_inverse_attributes = {} @@ -122,6 +123,9 @@ class sqlite(file): return entity def by_type(self, type, include_subtypes=True): + # TODO use cached subtypes + import ifcopenshell.util.schema + if self.class_map: results = [] subtypes = self.ifc_class_subtypes[type] if include_subtypes else self.ifc_class_subtypes[type][0:1] @@ -167,7 +171,9 @@ class sqlite(file): return results def get_inverse(self, inst, allow_duplicate=False, with_attribute_indices=False): - query = f"SELECT inverses FROM {inst.sqlite_wrapper.ifc_class} WHERE `ifc_id` = {inst.sqlite_wrapper.id} LIMIT 1" + query = ( + f"SELECT inverses FROM {inst.sqlite_wrapper.ifc_class} WHERE `ifc_id` = {inst.sqlite_wrapper.id} LIMIT 1" + ) self.cursor.execute(query) row = self.cursor.fetchone() if not row or not row[0]: @@ -198,9 +204,9 @@ class sqlite(file): "verts": np.frombuffer(row["verts"]).tolist() if row["verts"] else [], "edges": np.frombuffer(row["edges"], dtype=np.int64).tolist() if row["edges"] else [], "faces": np.frombuffer(row["faces"], dtype=np.int64).tolist() if row["faces"] else [], - "material_ids": np.frombuffer(row["material_ids"], dtype=np.int64).tolist() - if row["material_ids"] - else [], + "material_ids": ( + np.frombuffer(row["material_ids"], dtype=np.int64).tolist() if row["material_ids"] else [] + ), "materials": json.loads(row["materials"]) if row["materials"] else [], } shapes[row["ifc_id"]] = { @@ -353,7 +359,7 @@ class sqlite_entity(entity_instance): def get_info(self, include_identifier=True, recursive=False, return_type=dict, ignore=(), scalar_only=False): info = {"id": self.sqlite_wrapper.id, "type": self.sqlite_wrapper.ifc_class} if not self.sqlite_wrapper.attribute_cache: - self.__getitem__(0) # This will get all attributes + self.__getitem__(0) # This will get all attributes info.update(self.sqlite_wrapper.attribute_cache) return info diff --git a/src/ifcopenshell-python/ifcopenshell/util/file.py b/src/ifcopenshell-python/ifcopenshell/util/file.py deleted file mode 100644 index 2898f5448c..0000000000 --- a/src/ifcopenshell-python/ifcopenshell/util/file.py +++ /dev/null @@ -1,29 +0,0 @@ -# IfcOpenShell - IFC toolkit and geometry engine -# Copyright (C) 2021 Dion Moult -# -# This file is part of IfcOpenShell. -# -# IfcOpenShell is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# IfcOpenShell is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with IfcOpenShell. If not, see . - -from pathlib import Path - - -def guess_format(path: Path) -> "str | None": - """Try to guess format using file extension""" - if path.suffix.lower() in (".ifczip", ".zip"): - return ".ifcZIP" - elif path.suffix.lower() in (".ifcxml", ".xml"): - return ".ifcXML" - elif path.suffix.lower() in (".ifcsqlite", ".sqlite", ".db"): - return ".ifcSQLite" From 89c4cbeb05550bc79e503950d6f6183629813d80 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 12:17:46 +1000 Subject: [PATCH 49/62] Drop support for Python 2. --- src/ifcopenshell-python/ifcopenshell/__init__.py | 4 ---- .../ifcopenshell/entity_instance.py | 4 ---- .../ifcopenshell/express/mapping.py | 2 -- .../ifcopenshell/express/nodes.py | 3 --- src/ifcopenshell-python/ifcopenshell/file.py | 14 +------------- .../ifcopenshell/geom/__init__.py | 3 --- src/ifcopenshell-python/ifcopenshell/geom/app.py | 4 ---- .../ifcopenshell/geom/code_editor_pane.py | 4 ---- src/ifcopenshell-python/ifcopenshell/geom/main.py | 5 ----- .../ifcopenshell/geom/occ_utils.py | 10 +--------- src/ifcopenshell-python/ifcopenshell/guid.py | 3 --- src/ifcopenshell-python/ifcopenshell/main.py | 4 ---- src/ifcopenshell-python/ifcopenshell/template.py | 4 ---- src/ifcopenshell-python/ifcopenshell/util/data.py | 1 - .../ifcopenshell/util/element.py | 1 - src/ifcopenshell-python/ifcopenshell/util/pset.py | 1 - src/ifcopenshell-python/ifcopenshell/validate.py | 2 -- 17 files changed, 2 insertions(+), 67 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 7c936f7b06..205c3c7659 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -30,10 +30,6 @@ Example: model = ifcopenshell.open("/path/to/model.ifc") """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import zipfile diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index b94aedac41..57a2ab528f 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -17,10 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import functools import importlib import numbers diff --git a/src/ifcopenshell-python/ifcopenshell/express/mapping.py b/src/ifcopenshell-python/ifcopenshell/express/mapping.py index f32cc0fae3..ffabee4e82 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/mapping.py +++ b/src/ifcopenshell-python/ifcopenshell/express/mapping.py @@ -17,8 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import print_function - import sys import nodes import templates diff --git a/src/ifcopenshell-python/ifcopenshell/express/nodes.py b/src/ifcopenshell-python/ifcopenshell/express/nodes.py index a6d29650f7..34d3ef747b 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/nodes.py +++ b/src/ifcopenshell-python/ifcopenshell/express/nodes.py @@ -17,13 +17,10 @@ # along with IfcOpenShell. If not, see . -from __future__ import print_function - import io import string import operator import collections - import bootstrap class Node: diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 40986a3735..0a9f854735 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -17,11 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import annotations - import os import re import numbers @@ -34,13 +29,6 @@ from typing import Optional, Any from . import ifcopenshell_wrapper from .entity_instance import entity_instance -try: - # Python 2 - basestring -except NameError: - # Python 3 or newer - basestring = (str, bytes) - class Transaction: def __init__(self, ifc_file): @@ -403,7 +391,7 @@ class file(object): def __getitem__(self, key): if isinstance(key, numbers.Integral): return entity_instance(self.wrapped_data.by_id(key), self) - elif isinstance(key, basestring): + elif isinstance(key, (str, bytes)): return entity_instance(self.wrapped_data.by_guid(str(key)), self) def by_id(self, id: int) -> ifcopenshell.entity_instance: diff --git a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py index 7edfeaa50b..69b7c344a8 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py @@ -18,9 +18,6 @@ """Geometry processing and analysis""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function def _has_occ(): diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py index fbba6063a1..28747a6667 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/app.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py @@ -16,10 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import time diff --git a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py index fd073e880d..808eb90b25 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py @@ -16,10 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import logging diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index cbc7f4ce21..23eb66cda7 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -17,11 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import annotations - import os import sys import operator diff --git a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py index 825157ab89..7d879acfc5 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py @@ -17,20 +17,12 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import random import operator import warnings from collections import namedtuple - -try: # python 3.3+ - from collections.abc import Iterable -except ImportError: # python 2 - from collections import Iterable +from collections.abc import Iterable import OCC diff --git a/src/ifcopenshell-python/ifcopenshell/guid.py b/src/ifcopenshell-python/ifcopenshell/guid.py index a5e417e251..ac0a2ed181 100644 --- a/src/ifcopenshell-python/ifcopenshell/guid.py +++ b/src/ifcopenshell-python/ifcopenshell/guid.py @@ -18,9 +18,6 @@ """Reads and writes encoded GlobalIds""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import uuid import string diff --git a/src/ifcopenshell-python/ifcopenshell/main.py b/src/ifcopenshell-python/ifcopenshell/main.py index f9d4dc94a8..632208f4d9 100644 --- a/src/ifcopenshell-python/ifcopenshell/main.py +++ b/src/ifcopenshell-python/ifcopenshell/main.py @@ -17,10 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from . import ifcopenshell_wrapper version = ifcopenshell_wrapper.version() diff --git a/src/ifcopenshell-python/ifcopenshell/template.py b/src/ifcopenshell-python/ifcopenshell/template.py index a7a77ebae6..7e506e3fd6 100644 --- a/src/ifcopenshell-python/ifcopenshell/template.py +++ b/src/ifcopenshell-python/ifcopenshell/template.py @@ -17,10 +17,6 @@ # along with IfcOpenShell. If not, see . -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import time import uuid diff --git a/src/ifcopenshell-python/ifcopenshell/util/data.py b/src/ifcopenshell-python/ifcopenshell/util/data.py index a66ea80252..b968879c58 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/data.py +++ b/src/ifcopenshell-python/ifcopenshell/util/data.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from __future__ import annotations import numpy as np import ifcopenshell from typing import Any, Union diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index e93a22907b..bb23d1c95a 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from __future__ import annotations import ifcopenshell import ifcopenshell.util.element from typing import Any, Callable, Optional, Union, Literal, overload diff --git a/src/ifcopenshell-python/ifcopenshell/util/pset.py b/src/ifcopenshell-python/ifcopenshell/util/pset.py index 4a8ce5c89d..6156af3f9c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/pset.py +++ b/src/ifcopenshell-python/ifcopenshell/util/pset.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -from __future__ import annotations import re import pathlib import ifcopenshell diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py index 2b7b8af5ea..fa51bbde15 100644 --- a/src/ifcopenshell-python/ifcopenshell/validate.py +++ b/src/ifcopenshell-python/ifcopenshell/validate.py @@ -32,8 +32,6 @@ Available flags: - ``--fields``: Output more detailed information about failed entities (available only with ``--json``). """ -from __future__ import print_function - import os import sys import json From 424a06f6c8ff929da4029d362cbb64a58afd69ed Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 14:58:12 +1000 Subject: [PATCH 50/62] More cleaning up of forward type hints to fix import errors --- .../ifcopenshell/__init__.py | 19 ++++++------------- .../ifcopenshell/api/root/create_entity.py | 1 + .../ifcopenshell/geom/main.py | 2 +- .../ifcopenshell/util/data.py | 2 +- .../ifcopenshell/util/pset.py | 6 +++--- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 205c3c7659..9042ff7d1d 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -35,9 +35,8 @@ import sys import zipfile import tempfile from pathlib import Path -from typing import Optional +from typing import Optional, Union -import ifcopenshell.util.file if hasattr(os, "uname"): platform_system = os.uname()[0].lower() @@ -56,16 +55,9 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "lib", p try: from . import ifcopenshell_wrapper -except Exception as e: - if int(python_version_tuple[0]) == 2: - # Only for py2, as py3 has exception chaining - import traceback - - traceback.print_exc() - print("-" * 64) +except Exception: raise ImportError("IfcOpenShell not built for '%s'" % python_distribution) -from . import guid from .file import file from .entity_instance import entity_instance, register_schema_attributes from .sql import sqlite, sqlite_entity @@ -92,11 +84,12 @@ class SchemaError(Error): pass -def open(path: "os.PathLike | str", format: str = None, should_stream: bool = False) -> file: +def open(path: Union[os.PathLike, str], format: Optional[str] = None, should_stream: bool = False) -> file: """Loads an IFC dataset from a filepath - You can specify a file format. If no format is given, it is guessed from its extension. - Currently supported specified format : .ifc | .ifcZIP | .ifcXML + You can specify a file format. If no format is given, it is guessed from + its extension. Currently supported specified format: .ifc | .ifcZIP | + .ifcXML. You can then filter by element ID, class, etc, and subscript by id or guid. diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py index 5bb64d411a..c1ab6c77e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py @@ -18,6 +18,7 @@ import ifcopenshell import ifcopenshell.api +import ifcopenshell.guid from typing import Optional diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 23eb66cda7..3cd1cb3c5c 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -148,7 +148,7 @@ class tree(ifcopenshell_wrapper.tree): def select( self, value: Union[ - entity_instance, ifcopenshell_wrapper.BRepElement, tuple[float, float, float], TopoDS.TopoDS_Shape + entity_instance, ifcopenshell_wrapper.BRepElement, tuple[float, float, float], "TopoDS.TopoDS_Shape" ], **kwargs, ) -> list[entity_instance]: diff --git a/src/ifcopenshell-python/ifcopenshell/util/data.py b/src/ifcopenshell-python/ifcopenshell/util/data.py index b968879c58..365f0df2cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/data.py +++ b/src/ifcopenshell-python/ifcopenshell/util/data.py @@ -30,7 +30,7 @@ class Clipping: operand_type: str = "IfcHalfSpaceSolid" @classmethod - def parse(cls, raw_data: Any) -> Union[ifcopenshell.entity_instance, Clipping, None]: + def parse(cls, raw_data: Any) -> Union[ifcopenshell.entity_instance, "Clipping", None]: """Parse various formats into a clipping object `raw_data` can be either: diff --git a/src/ifcopenshell-python/ifcopenshell/util/pset.py b/src/ifcopenshell-python/ifcopenshell/util/pset.py index 6156af3f9c..97db6ff4b2 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/pset.py +++ b/src/ifcopenshell-python/ifcopenshell/util/pset.py @@ -23,12 +23,12 @@ import ifcopenshell.util.schema import ifcopenshell.util.type from ifcopenshell.entity_instance import entity_instance from functools import lru_cache -from typing import List, Generator, Optional +from typing import List, Optional -templates: dict[str, PsetQto] = {} +templates: dict[str, "PsetQto"] = {} -def get_template(schema: str) -> PsetQto: +def get_template(schema: str) -> "PsetQto": global templates if schema not in templates: templates[schema] = PsetQto(schema) From f6c2e2c20d978a33233940612de32b8ba7ed4110 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 15:44:06 +1000 Subject: [PATCH 51/62] Fix #4530. Fix various styling issues on docs. --- src/blenderbim/docs/_static/custom.css | 4 ++++ src/blenderbim/docs/conf.py | 6 ++++++ src/ifcopenshell-python/docs/_static/custom.css | 4 ++++ src/ifcopenshell-python/docs/conf.py | 6 ++++++ 4 files changed, 20 insertions(+) diff --git a/src/blenderbim/docs/_static/custom.css b/src/blenderbim/docs/_static/custom.css index 03f5b9016f..f6939f3ac5 100644 --- a/src/blenderbim/docs/_static/custom.css +++ b/src/blenderbim/docs/_static/custom.css @@ -8,6 +8,9 @@ h1, h2, h3, h4 { -webkit-background-clip: text; -webkit-text-fill-color: transparent; } +h1 code.literal { + background: none; +} a { text-decoration: none; } @@ -16,6 +19,7 @@ a { } .sidebar-brand-text { font-size: 1rem; + text-align: center; } .blockbutton { max-width: 500px; diff --git a/src/blenderbim/docs/conf.py b/src/blenderbim/docs/conf.py index 69afbca790..aaf5908bf0 100644 --- a/src/blenderbim/docs/conf.py +++ b/src/blenderbim/docs/conf.py @@ -95,7 +95,10 @@ html_theme_options = { "color-background-border": "#cfd0cb", "color-foreground-primary": "#2e3436", "color-sidebar-item-background--hover": "#f7f7f6", + "color-link": "#39b54a", + "color-link--visited": "#39b54a", "color-link--hover": "#d98014", + "color-link--visited--hover": "#d98014", "font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji" }, "dark_css_variables": { @@ -106,7 +109,10 @@ html_theme_options = { "color-background-border": "#2e3436", "color-foreground-primary": "#eeeeec", "color-sidebar-item-background--hover": "#2e3436", + "color-link": "#39b54a", + "color-link--visited": "#39b54a", "color-link--hover": "#d98014", + "color-link--visited--hover": "#d98014", "font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji" }, diff --git a/src/ifcopenshell-python/docs/_static/custom.css b/src/ifcopenshell-python/docs/_static/custom.css index c1e237ec6c..a32a849707 100644 --- a/src/ifcopenshell-python/docs/_static/custom.css +++ b/src/ifcopenshell-python/docs/_static/custom.css @@ -8,6 +8,9 @@ h1, h2, h3, h4 { -webkit-background-clip: text; -webkit-text-fill-color: transparent; } +h1 code.literal { + background: none; +} a { text-decoration: none; } @@ -16,6 +19,7 @@ a { } .sidebar-brand-text { font-size: 1rem; + text-align: center; } .blockbutton { max-width: 500px; diff --git a/src/ifcopenshell-python/docs/conf.py b/src/ifcopenshell-python/docs/conf.py index 23b45563c1..6537ba29f8 100644 --- a/src/ifcopenshell-python/docs/conf.py +++ b/src/ifcopenshell-python/docs/conf.py @@ -130,7 +130,10 @@ html_theme_options = { "color-background-border": "#cfd0cb", "color-foreground-primary": "#2e3436", "color-sidebar-item-background--hover": "#f7f7f6", + "color-link": "#39b54a", + "color-link--visited": "#39b54a", "color-link--hover": "#d98014", + "color-link--visited--hover": "#d98014", "font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji" }, "dark_css_variables": { @@ -141,7 +144,10 @@ html_theme_options = { "color-background-border": "#2e3436", "color-foreground-primary": "#eeeeec", "color-sidebar-item-background--hover": "#2e3436", + "color-link": "#39b54a", + "color-link--visited": "#39b54a", "color-link--hover": "#d98014", + "color-link--visited--hover": "#d98014", "font-stack": "Nunito, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji" }, From 26434f0331dad5b5b5d2ff9a07a260b790baed6c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 15:51:48 +1000 Subject: [PATCH 52/62] Fix #4589. Symlink entire ifcopenshell dir for dev setups. --- src/blenderbim/docs/devs/installation.rst | 25 +++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/blenderbim/docs/devs/installation.rst b/src/blenderbim/docs/devs/installation.rst index ba881b51f5..d3fe796316 100644 --- a/src/blenderbim/docs/devs/installation.rst +++ b/src/blenderbim/docs/devs/installation.rst @@ -92,13 +92,14 @@ For Linux or Mac: $ ln -s $PWD/src/blenderbim/blenderbim/tool $BLENDER_ADDON_PATH/tool $ ln -s $PWD/src/blenderbim/blenderbim/bim $BLENDER_ADDON_PATH/bim - # Remove the IfcOpenShell dependency Python code - $ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/api - $ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/util + # Copy over compiled IfcOpenShell files + $ cp $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/*_wrapper* $PWD/src/ifcopenshell-python/ifcopenshell/ + + # Remove the IfcOpenShell dependency + $ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell # Replace them with links to the Git repository - $ ln -s $PWD/src/ifcopenshell-python/ifcopenshell/api $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/api - $ ln -s $PWD/src/ifcopenshell-python/ifcopenshell/util $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/util + $ ln -s $PWD/src/ifcopenshell-python/ifcopenshell $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell # Remove and link other IfcOpenShell utilities $ rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py @@ -153,21 +154,19 @@ Before running it follow the instructions descibed after `rem` tags. rd /S /Q "%blenderbim%\tool\" rd /S /Q "%blenderbim%\bim\" - echo Replacing them with links to the Git repository... mklink /D "%blenderbim%\core" "%cd%\src\blenderbim\blenderbim\core" mklink /D "%blenderbim%\tool" "%cd%\src\blenderbim\blenderbim\tool" mklink /D "%blenderbim%\bim" "%cd%\src\blenderbim\blenderbim\bim" + echo Copy over compiled IfcOpenShell files... + copy %blenderbim%\libs\site\packages\ifcopenshell\*_wrapper* %cd%\src\ifcopenshell-python\ifcopenshell\ - echo Remove the IfcOpenShell dependency Python code... - rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell\api" - rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell\util" + echo Remove the IfcOpenShell dependency... + rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell" - - echo Replacing them with links to the Git repository... - mklink /D "%blenderbim%\libs\site\packages\ifcopenshell\api" "%cd%\src\ifcopenshell-python\ifcopenshell\api" - mklink /D "%blenderbim%\libs\site\packages\ifcopenshell\util" "%cd%\src\ifcopenshell-python\ifcopenshell\util" + echo Replace them with links to the Git repository... + mklink /D "%blenderbim%\libs\site\packages\ifcopenshell" "%cd%\src\ifcopenshell-python\ifcopenshell" echo Remove and link other IfcOpenShell utilities... del "%blenderbim%\libs\site\packages\ifccsv.py" From 0a3dddef2f793c8ca1efdfc0d2b00a40956caf72 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 16:03:07 +1000 Subject: [PATCH 53/62] More Python 2 to Python 3 upgrades --- src/ifcopenshell-python/ifcopenshell/entity_instance.py | 2 +- src/ifcopenshell-python/ifcopenshell/express/codegen.py | 2 +- src/ifcopenshell-python/ifcopenshell/file.py | 2 +- src/ifcopenshell-python/ifcopenshell/geom/app.py | 2 +- src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index 57a2ab528f..a0d44b1b23 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -100,7 +100,7 @@ for nm in ifcopenshell_wrapper.schema_names(): register_schema_attributes(schema) -class entity_instance(object): +class entity_instance: """Base class for all IFC objects. An instantiated entity_instance will have methods of Python and the IFC class itself. diff --git a/src/ifcopenshell-python/ifcopenshell/express/codegen.py b/src/ifcopenshell-python/ifcopenshell/express/codegen.py index efdd9c918d..fe997091b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/codegen.py +++ b/src/ifcopenshell-python/ifcopenshell/express/codegen.py @@ -29,7 +29,7 @@ def indent(n, s): return "\n".join(" "*n + l for l in splitted) -class Base(object): +class Base: """ A base class for all code generation classes. Currently only working around some python 2/3 incompatibilities in terms of unicode file handling. diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 0a9f854735..c1ba69c22c 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -176,7 +176,7 @@ class Transaction: file_dict = {} -class file(object): +class file: """Base class for containing IFC files. Class has instance methods for filtering by element Id, Type, etc. diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py index 28747a6667..298b5b37ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/app.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py @@ -122,7 +122,7 @@ class geometry_creation_thread(QtCore.QThread): self.signals.completed.emit((it, self.f, list(_()))) -class configuration(object): +class configuration: def __init__(self): try: import ConfigParser diff --git a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py index 808eb90b25..f84f45a234 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/code_editor_pane.py @@ -45,7 +45,7 @@ except BaseException: CodeEdit = QtWidgets.QPlainTextEdit -class StdoutRedirector(object): +class StdoutRedirector: """A class for redirecting stdout to this Text widget.""" def __init__(self, widget): From 722201a1aff162924a46f4dd366af222319644e4 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 17:15:58 +1000 Subject: [PATCH 54/62] Add py.typed for static analysis with mypy --- src/ifcopenshell-python/ifcopenshell/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/py.typed diff --git a/src/ifcopenshell-python/ifcopenshell/py.typed b/src/ifcopenshell-python/ifcopenshell/py.typed new file mode 100644 index 0000000000..e69de29bb2 From d76462ca4290c7d29abbba451f95feaf66581683 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 17:32:47 +1000 Subject: [PATCH 55/62] Write more documentation Sphinx autoapi also now only shows subpackages 1 level deep. This prevents us having a huge long list. Also don't show private or special members. Also show imported members so ifcopenshell.file and ifcopenshell.entity_instance works in docs too. --- .../docs/_autoapi_templates/index.rst | 16 +++ .../docs/_autoapi_templates/python/module.rst | 114 ++++++++++++++++++ src/ifcopenshell-python/docs/conf.py | 5 +- .../ifcopenshell/__init__.py | 38 +++++- .../ifcopenshell/api/__init__.py | 22 ++-- .../ifcopenshell/entity_instance.py | 47 ++++---- src/ifcopenshell-python/ifcopenshell/file.py | 28 +++-- .../ifcopenshell/geom/__init__.py | 10 +- src/ifcopenshell-python/ifcopenshell/guid.py | 8 +- src/ifcopenshell-python/ifcopenshell/main.py | 23 ---- .../ifcopenshell/util/__init__.py | 11 +- 11 files changed, 248 insertions(+), 74 deletions(-) create mode 100644 src/ifcopenshell-python/docs/_autoapi_templates/index.rst create mode 100644 src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst delete mode 100644 src/ifcopenshell-python/ifcopenshell/main.py diff --git a/src/ifcopenshell-python/docs/_autoapi_templates/index.rst b/src/ifcopenshell-python/docs/_autoapi_templates/index.rst new file mode 100644 index 0000000000..8a3234fefc --- /dev/null +++ b/src/ifcopenshell-python/docs/_autoapi_templates/index.rst @@ -0,0 +1,16 @@ +Python API Reference +==================== + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + {% for page in pages %} + {% if page.top_level_object and page.display %} + {{ page.include_path }} + {% endif %} + {% endfor %} + +.. [#f1] Created with `sphinx-autoapi `_ diff --git a/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst b/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst new file mode 100644 index 0000000000..c522bf2092 --- /dev/null +++ b/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst @@ -0,0 +1,114 @@ +{% if not obj.display %} +:orphan: + +{% endif %} +:py:mod:`{{ obj.name }}` +=========={{ "=" * obj.name|length }} + +.. py:module:: {{ obj.name }} + +{% if obj.docstring %} +.. autoapi-nested-parse:: + + {{ obj.docstring|indent(3) }} + +{% endif %} + +{% block subpackages %} +{% set visible_subpackages = obj.subpackages|selectattr("display")|list %} +{% if visible_subpackages %} +Subpackagesa +------------ +.. toctree:: + :titlesonly: + :maxdepth: 1 + +{% for subpackage in visible_subpackages %} + {{ subpackage.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block submodules %} +{% set visible_submodules = obj.submodules|selectattr("display")|list %} +{% if visible_submodules %} +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + +{% for submodule in visible_submodules %} + {{ submodule.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block content %} +{% if obj.all is not none %} +{% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %} +{% elif obj.type is equalto("package") %} +{% set visible_children = obj.children|selectattr("display")|list %} +{% else %} +{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %} +{% endif %} +{% if visible_children %} +{{ obj.type|title }} Contents +{{ "-" * obj.type|length }}--------- + +{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %} +{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %} +{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %} +{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %} +{% block classes scoped %} +{% if visible_classes %} +Classes +~~~~~~~ + +.. autoapisummary:: + +{% for klass in visible_classes %} + {{ klass.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block functions scoped %} +{% if visible_functions %} +Functions +~~~~~~~~~ + +.. autoapisummary:: + +{% for function in visible_functions %} + {{ function.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block attributes scoped %} +{% if visible_attributes %} +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + +{% for attribute in visible_attributes %} + {{ attribute.id }} +{% endfor %} + + +{% endif %} +{% endblock %} +{% endif %} +{% for obj_item in visible_children %} +{{ obj_item.render()|indent(0) }} +{% endfor %} +{% endif %} +{% endblock %} diff --git a/src/ifcopenshell-python/docs/conf.py b/src/ifcopenshell-python/docs/conf.py index 6537ba29f8..9106e4359e 100644 --- a/src/ifcopenshell-python/docs/conf.py +++ b/src/ifcopenshell-python/docs/conf.py @@ -74,6 +74,9 @@ autoapi_dirs = ['../ifcopenshell', '../../bcf/src', '../../bsdd', '../../ifccsv' # These are auto-generated based on the IFC schema, so exclude them autoapi_ignore = ['*ifcopenshell/express/rules*'] +# Custom autoapi templates to make it easier to read our docs +autoapi_template_dir = "_autoapi_templates" + # autoapi_options doesn't have show-module-summary, as it tends to create one # page per function which contradicts the presentation of showing all functions # as a list. This creates two possible locations where a function is documented @@ -81,7 +84,7 @@ autoapi_ignore = ['*ifcopenshell/express/rules*'] # ifcopenshell.file is imported from ifcopenshell.file.file, but it gets pretty # confusing to see the docs again in multiple places (seriously, # ifcopenshell.file.file is everywhere). -autoapi_options = ['members', 'undoc-members', 'private-members', 'special-members', 'show-inheritance'] +autoapi_options = ['members', 'undoc-members', 'show-inheritance', 'imported-members'] # This option is set to both to allow both class docstrings and __init__ docstrings. autoapi_python_class_content = 'both' diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 9042ff7d1d..75f5f426f4 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -16,18 +16,42 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -"""The entry module for IfcOpenShell +"""Welcome to IfcOpenShell! IfcOpenShell provides a way to read and write IFCs. -Typically used for opening an IFC via a filepath, or accessing one of the -submodules. +IfcOpenShell can open IFC files, read entities (such as walls, buildings, +properties, systems, etc), edit attributes, write out ``.ifc`` files and more. + +This module provides primitive functions to interact with IFC, including: + +- For most users, you can open and read IFC models, see docs for :func:`open`. + This returns an :class:`file` object representing the IFC model. You can then + query the model to filter elements. +- For developers, you can query the schema itself, see docs for + :func:`schema_by_name`. This returns a schema object which you can use to + analyse the definitions of IFC classes and data types. + +You may also be interested in: + +- For model authoring and editing operations, see :mod:`ifcopenshell.api`. +- For extracting information from models, see :mod:`ifcopenshell.util`. +- For processing geometry, see :mod:`ifcopenshell.geom`. + + +For more details, consult https://docs.ifcopenshell.org/ Example: .. code:: python import ifcopenshell + print(ifcopenshell.version) # v0.7.0-1b1fd1e6 + model = ifcopenshell.open("/path/to/model.ifc") + walls = model.by_type("IfcWall") + + for wall in walls: + print(wall.Name) """ import os @@ -219,7 +243,7 @@ def schema_by_name( def guess_format(path: Path) -> Union[str | None]: - """Try to guess format using file extension + """Guesses the IFC format using file extension IFCs may be serialised as different formats. The most common is a ``.ifc`` file, which is plaintext and stores data using the STEP Physical File @@ -229,6 +253,9 @@ def guess_format(path: Path) -> Union[str | None]: has the extension of .xml or .ifcxml (case insensitive), it will return .ifcXML. + Users generally won't call this function. The :func:`open` function uses + this internally to guess the file format. + :return: Either .ifc, .ifcZIP, .ifcXML, .ifcJSON, .ifcSQLite, or None. """ suffix = path.suffix.lower() @@ -245,4 +272,5 @@ def guess_format(path: Path) -> Union[str | None]: return None -from .main import * +version = ifcopenshell_wrapper.version() +get_log = ifcopenshell_wrapper.get_log diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index dcfbd1821c..a4e99ad318 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -16,7 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -"""High level user-oriented IFC authoring capabilities""" +"""High level IFC authoring and editing functions + +Authoring, editing, and deleting IFC data requires a detailed understanding of +the rules of the IFC schema. This API module provides simple to use authoring +functions that hide this complexity from you. Things like managing differences +between IFC versions, tracking owernship changes, or cleaning up after orphaned +relationships are all handled automatically. +""" import json import numpy @@ -24,13 +31,12 @@ import pkgutil import inspect import importlib import ifcopenshell -import ifcopenshell.api from typing import Callable, Any, Optional from functools import partial -pre_listeners = {} -post_listeners = {} +pre_listeners: dict[str, dict] = {} +post_listeners: dict[str, dict] = {} def batching_argument_deprecation( @@ -128,8 +134,8 @@ ARGUMENTS_DEPRECATION = { } -CACHED_USECASE_CLASSES = {} -CACHED_USECASES = {} +CACHED_USECASE_CLASSES: dict[str, Callable] = {} +CACHED_USECASES: dict[str, Callable] = {} def run( @@ -250,8 +256,6 @@ def extract_docs(module, usecase): import typing import collections - results = [] - inputs = collections.OrderedDict() function_init = getattr(getattr(ifcopenshell.api, module), usecase).Usecase.__init__ @@ -307,7 +311,7 @@ def wrap_usecase(usecase_path, usecase): try: result = usecase(*args, **settings) except TypeError as e: - msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(Usecase.__init__)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation." + msg = f"Incorrect function arguments provided for {usecase_path}\n{str(e)}. You specified args {args} and settings {settings}\n\nCorrect signature is {inspect.signature(usecase)}\nSee help(ifcopenshell.api.{usecase_path}) for documentation." raise TypeError(msg) from e if should_run_listeners: diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index a0d44b1b23..c1c4fd79b4 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -196,33 +196,36 @@ class entity_instance: @staticmethod def walk(f: Callable[[Any], bool], g: Callable[[Any], Any], value: Any) -> Any: - """ - Applies transformation to `value` based on a given condition. - If value is a nested structure (e.g., a list or a tuple) will apply transformation to it's elements. - . + """Applies a transformation to `value` based on a given condition. - :param f: A callable that takes a single argument and returns a boolean value. It represents the condition - :type f: Callable - :param g: A callable that takes a single argument and returns a transformed value. It represents the transformation - :type g: Callable - :param value: Any object, the input value to be processed - :type value: Any - :return: Transformed value - :rtype: Any + If value is a nested structure (e.g., a list or a tuple) will apply + transformation to it's elements. - Example: + :param f: A callable that takes a single argument and returns a boolean + value. It represents the condition. + :type f: Callable + :param g: A callable that takes a single argument and returns a + transformed value. It represents the transformation. + :type g: Callable + :param value: Any object, the input value to be processed + :type value: Any + :return: Transformed value + :rtype: Any - .. code:: python + Example: - # Define condition and transformation functions - condition = lambda v: v == old - transform = lambda v: new + .. code:: python - # Usage example - attribute_value = element.RelatedElements - print(old in attribute_value, new in attribute_value) # True, False - result = element.walk(condition, transform, element.RelatedElements) - print(old in attribute_value, new in attribute_value) # False, True + # Define condition and transformation functions + condition = lambda v: v == old + transform = lambda v: new + + # Usage example + attribute_value = element.RelatedElements + print(old in attribute_value, new in attribute_value) # True, False + + result = element.walk(condition, transform, element.RelatedElements) + print(old in attribute_value, new in attribute_value) # False, True """ if isinstance(value, (tuple, list)): diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index c1ba69c22c..85be898392 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -550,20 +550,26 @@ class file: def __iter__(self): return iter(self[id] for id in self.wrapped_data.entity_names()) - def write(self, path: "os.PathLike | str", format=None, zipped=False) -> None: + def write(self, path: "os.PathLike | str", format: Optional[str] = None, zipped: bool = False) -> None: """Write ifc model to file. - :param format: Force use of a specific format. Guessed from file name if None. - Supported formats : .ifc, .ifcXML, .ifcZIP (equivalent to format=".ifc" with zipped=True) - For zipped .ifcXML use format=".ifcXML" with zipped=True + :param format: Force use of a specific format. Guessed from file name + if None. Supported formats : .ifc, .ifcXML, .ifcZIP (equivalent to + format=".ifc" with zipped=True) For zipped .ifcXML use + format=".ifcXML" with zipped=True + :type format: str :param zipped: zip the file after it is written + :type zipped: bool - Examples: - >>> model.write("path/to/model.ifc") - >>> model.write("path/to/model.ifcXML") - >>> model.write("path/to/model.ifcZIP") - >>> model.write("path/to/model.ifcZIP", format=".ifcXML", zipped=True) - >>> model.write("path/to/model.anyextension", format=".ifcXML") + Example: + + .. code:: python + + model.write("path/to/model.ifc") + model.write("path/to/model.ifcXML") + model.write("path/to/model.ifcZIP") + model.write("path/to/model.ifcZIP", format=".ifcXML", zipped=True) + model.write("path/to/model.anyextension", format=".ifcXML") """ path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) @@ -595,7 +601,7 @@ class file: return @staticmethod - def from_string(s: str) -> file: + def from_string(s: str) -> "file": return file(ifcopenshell_wrapper.read(s)) @staticmethod diff --git a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py index 69b7c344a8..38021e325f 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/__init__.py @@ -16,8 +16,16 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -"""Geometry processing and analysis""" +"""Geometry processing and analysis +IFC may define geometry explicitly (such as meshes) or implicitly (such as +parametric extrusions). This module provides methods to extract geometric +definitions in IFC into explicitly tessellated triangles or OpenCASCADE Breps +for further processing. + +This is typically needed when writing software to visualise or analyse +geometry. See also :mod:`ifcopenshell.util.shape` for deriving quantities. +""" def _has_occ(): diff --git a/src/ifcopenshell-python/ifcopenshell/guid.py b/src/ifcopenshell-python/ifcopenshell/guid.py index ac0a2ed181..eb11f31ba1 100644 --- a/src/ifcopenshell-python/ifcopenshell/guid.py +++ b/src/ifcopenshell-python/ifcopenshell/guid.py @@ -16,8 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -"""Reads and writes encoded GlobalIds""" +"""Reads and writes encoded GlobalIds +IFC entities may be identified using a unique ID (called a UUID or GUID). This +128-bit label is often represented in the form +xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. However, in IFC, it is also usually +stored as a 22 character base 64 encoded string. This module lets you convert +between these representations and generate new UUIDs. +""" import uuid import string diff --git a/src/ifcopenshell-python/ifcopenshell/main.py b/src/ifcopenshell-python/ifcopenshell/main.py deleted file mode 100644 index 632208f4d9..0000000000 --- a/src/ifcopenshell-python/ifcopenshell/main.py +++ /dev/null @@ -1,23 +0,0 @@ -# IfcOpenShell - IFC toolkit and geometry engine -# Copyright (C) 2021 Thomas Krijnen -# -# This file is part of IfcOpenShell. -# -# IfcOpenShell is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# IfcOpenShell is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with IfcOpenShell. If not, see . - - -from . import ifcopenshell_wrapper - -version = ifcopenshell_wrapper.version() -get_log = ifcopenshell_wrapper.get_log diff --git a/src/ifcopenshell-python/ifcopenshell/util/__init__.py b/src/ifcopenshell-python/ifcopenshell/util/__init__.py index 944d7db18c..bcd1e83caf 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/util/__init__.py @@ -16,4 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -"""Utility functions for common IFC queries""" +"""Utility functions for extracting IFC data + +Data in IFC files is represented using relationships between IFC entities. To +extract data like "what properties does this wall have" involves looping +through these relationships which can be tedious. + +This module makes it easy to get commonly requested data from IFC +relationships, such as properties of a wall, what elements are connected to +pipes, dates from work schedules, filtering maintainable elements, and more. +""" From 93639e9e50e28dceaf0f9db8f252a4ae52e07693 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 19:08:13 +1000 Subject: [PATCH 56/62] Even more cleaning of documentation references --- src/blenderbim/docs/_static/custom.css | 3 - .../docs/_autoapi_templates/python/module.rst | 4 +- .../docs/_static/custom.css | 26 +++- .../docs/introduction/how_to_contribute.rst | 7 +- .../ifcopenshell/__init__.py | 4 +- .../ifcopenshell/entity_instance.py | 46 +++++-- src/ifcopenshell-python/ifcopenshell/file.py | 36 ++--- .../ifcopenshell/util/constraint.py | 12 +- .../ifcopenshell/util/element.py | 128 +++++++++--------- .../ifcopenshell/util/geolocation.py | 10 +- .../ifcopenshell/util/placement.py | 10 +- .../ifcopenshell/util/representation.py | 4 +- .../ifcopenshell/util/selector.py | 6 +- .../ifcopenshell/util/shape.py | 16 +-- .../ifcopenshell/util/unit.py | 8 +- 15 files changed, 181 insertions(+), 139 deletions(-) diff --git a/src/blenderbim/docs/_static/custom.css b/src/blenderbim/docs/_static/custom.css index f6939f3ac5..6e5709c7c9 100644 --- a/src/blenderbim/docs/_static/custom.css +++ b/src/blenderbim/docs/_static/custom.css @@ -8,9 +8,6 @@ h1, h2, h3, h4 { -webkit-background-clip: text; -webkit-text-fill-color: transparent; } -h1 code.literal { - background: none; -} a { text-decoration: none; } diff --git a/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst b/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst index c522bf2092..cbd5f30094 100644 --- a/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst +++ b/src/ifcopenshell-python/docs/_autoapi_templates/python/module.rst @@ -17,8 +17,8 @@ {% block subpackages %} {% set visible_subpackages = obj.subpackages|selectattr("display")|list %} {% if visible_subpackages %} -Subpackagesa ------------- +Subpackages +----------- .. toctree:: :titlesonly: :maxdepth: 1 diff --git a/src/ifcopenshell-python/docs/_static/custom.css b/src/ifcopenshell-python/docs/_static/custom.css index a32a849707..1c1de1de08 100644 --- a/src/ifcopenshell-python/docs/_static/custom.css +++ b/src/ifcopenshell-python/docs/_static/custom.css @@ -51,14 +51,32 @@ section img { box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; border-radius: 5px; } + +/* Make it clearer which signatures are part of a class */ .py.class { - /* Make it clearer which signatures are part of a class */ border-left: 3px solid var(--color-brand-primary); } -.py.function, .py.method { - /* Make it clearer which signatures are part of a method or function */ - border-left: 3px solid var(--color-background-item); +.py.class > .sig { + background: var(--color-brand-primary) !important; + margin: 0; + border-radius: 0; } +.py.class > .sig * { + color: #2e3436 !important; +} +.py.class > .sig a { + color: #fff; +} + +/* Make it easier to spot functions and methods */ +.py.function, .py.method { + border-top: 1px solid var(--color-background-item); +} +dl.py.property, dl.py.attribute, dl.py.method, dl.py.function { + padding-top: 10px; + padding-bottom: 10px; +} + .field-list > dt { /* Clearly distinguish parameters otherwise it looks like a wall of text */ color: var(--color-brand-content); diff --git a/src/ifcopenshell-python/docs/introduction/how_to_contribute.rst b/src/ifcopenshell-python/docs/introduction/how_to_contribute.rst index 59015fe339..212eb0cba8 100644 --- a/src/ifcopenshell-python/docs/introduction/how_to_contribute.rst +++ b/src/ifcopenshell-python/docs/introduction/how_to_contribute.rst @@ -21,14 +21,15 @@ Python API documentation is autogenerated from docstrings present in the source code of the respective Python module. If you want to build the documentation locally, the documentation system uses -`Sphinx `_. First, install the theme and -theme dependencies: +`Sphinx `_. First, install Sphinx and +dependencies: .. code-block:: console - $ pip install furo + $ pip install sphinx $ pip install sphinx-autoapi $ pip install sphinx-copybutton + $ pip install furo Now you can generate the documentation: diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 75f5f426f4..739e99bbc6 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -168,7 +168,7 @@ def create_entity(type, schema="IFC4", *args, **kwargs): """Creates a new IFC entity that does not belong to an IFC file object Note that it is more common to create entities within a existing file - object. See :meth:`ifcopenshell.file.file.create_entity`. + object. See :meth:`ifcopenshell.file.create_entity`. :param type: Case insensitive name of the IFC class :type type: string @@ -177,7 +177,7 @@ def create_entity(type, schema="IFC4", *args, **kwargs): :param args: The positional arguments of the IFC class :param kwargs: The keyword arguments of the IFC class :returns: An entity instance - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index c1c4fd79b4..05ad3fd984 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -22,7 +22,6 @@ import importlib import numbers import itertools import operator -import functools import subprocess import sys import time @@ -33,7 +32,7 @@ from . import settings try: import logging -except ImportError as e: +except ImportError: logging = type("logger", (object,), {"exception": staticmethod(lambda s: print(s))}) T = TypeVar("T") @@ -101,20 +100,47 @@ for nm in ifcopenshell_wrapper.schema_names(): class entity_instance: - """Base class for all IFC objects. + """Represents an entity (wall, slab, property, etc) of an IFC model - An instantiated entity_instance will have methods of Python and the IFC class itself. + An IFC model consists of entities. Examples of entities include walls, + slabs, doors and so on. Entities can also be non-physical things, like + properties, systems, construction tasks, colours, geometry, and more. + + Entities are defined through an **IFC Class**. There are hundreds of **IFC + Classes** defined as part of the ISO standard by the buildingSMART + International organisation. The **IFC Class** defines the attributes of an + entity, as well as the data types and whether or not an attribute is + mandatory or optional. + + IfcOpenShell's API dynamically implements the IFC schema. You will not find + documentation about available **IFC Classes**, or what attributes they + have. Please consult the buildingSMART official documentation or start + reading :doc:`/introduction/introduction_to_ifc`. + + In addition to the Python methods you see documented here, an instantiated + entity_instance will have attributes defined by its IFC class. For example, + an entity instance which is an IfcWall class will have a ``Name`` + attribute, and an IfcColourRgb will have a ``Red`` attribute. Please + consult the buildingSMART official documentation. Example: .. code:: python - ifc_file = ifcopenshell.open(file_path) - products = ifc_file.by_type("IfcProduct") - print(products[0].__class__) - >>> - print(products[0].Representation) - >>> #423=IfcProductDefinitionShape($,$,(#409,#421)) + model = ifcopenshell.open(file_path) + walls = model.by_type("IfcWall") + wall = walls[0] + + print(wall) # #38=IFCWALL('2MEinnTPbCMwLOgceaQZFu',$,$,'My Wall',$,#52,#47,$,$); + print(wall.is_a()) # IfcWall + + # Note: the `Name` attribute is dynamic, based on the IFC class. + print(wall.Name) # My Wall + + # Attributes are ordered and may also be accessed via index. + print(wall[3]) # My Wall + + print(wall.__class__) # """ wrapped_data: ifcopenshell_wrapper.entity_instance diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 85be898392..fc2fb29f1c 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -307,7 +307,7 @@ class file: :param args: The positional arguments of the IFC class :param kwargs: The keyword arguments of the IFC class :returns: An entity instance - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: @@ -402,8 +402,8 @@ class file: :raises RuntimeError: If `id` is not found. - :returns: An ifcopenshell.entity_instance.entity_instance - :rtype: ifcopenshell.entity_instance.entity_instance + :returns: An ifcopenshell.entity_instance + :rtype: ifcopenshell.entity_instance """ return self[id] @@ -415,8 +415,8 @@ class file: :raises RuntimeError: If `guid` is not found. - :returns: An ifcopenshell.entity_instance.entity_instance - :rtype: ifcopenshell.entity_instance.entity_instance + :returns: An ifcopenshell.entity_instance + :rtype: ifcopenshell.entity_instance """ return self[guid] @@ -426,9 +426,9 @@ class file: If the entity already exists, it is not re-added. Existence of entity is checked by it's `.identity()`. :param inst: The entity instance to add - :type inst: ifcopenshell.entity_instance.entity_instance - :returns: An ifcopenshell.entity_instance.entity_instance - :rtype: ifcopenshell.entity_instance.entity_instance + :type inst: ifcopenshell.entity_instance + :returns: An ifcopenshell.entity_instance + :rtype: ifcopenshell.entity_instance """ if self.transaction: @@ -452,8 +452,8 @@ class file: :raises RuntimeError: If `type` is not found in IFC schema. - :returns: A list of ifcopenshell.entity_instance.entity_instance objects - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :returns: A list of ifcopenshell.entity_instance objects + :rtype: list[ifcopenshell.entity_instance] """ if include_subtypes: return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)] @@ -465,13 +465,13 @@ class file: """Get a list of all referenced instances for a particular instance including itself :param inst: The entity instance to get all sub instances - :type inst: ifcopenshell.entity_instance.entity_instance + :type inst: ifcopenshell.entity_instance :param max_levels: How far deep to recursively fetch sub instances. None or -1 means infinite. :type max_levels: None|int :param breadth_first: Whether to use breadth-first search, the default is depth-first. :type max_levels: bool - :returns: A list of ifcopenshell.entity_instance.entity_instance objects - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :returns: A list of ifcopenshell.entity_instance objects + :rtype: list[ifcopenshell.entity_instance] """ if max_levels is None: max_levels = -1 @@ -489,12 +489,12 @@ class file: """Return a list of entities that reference this entity :param inst: The entity instance to get inverse relationships - :type inst: ifcopenshell.entity_instance.entity_instance + :type inst: ifcopenshell.entity_instance :param allow_duplicate: Returns a `list` when True, `set` when False :param with_attribute_indices: Returns pairs of where i[idx] is inst or contains inst. Requires allow_duplicate=True - :returns: A list of ifcopenshell.entity_instance.entity_instance objects - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :returns: A list of ifcopenshell.entity_instance objects + :rtype: list[ifcopenshell.entity_instance] """ if with_attribute_indices and not allow_duplicate: raise ValueError("with_attribute_indices requires allow_duplicate to be True") @@ -514,7 +514,7 @@ class file: """Returns the number of entities that reference this entity :param inst: The entity instance to get inverse relationships - :type inst: ifcopenshell.entity_instance.entity_instance + :type inst: ifcopenshell.entity_instance :returns: The total number of references :rtype: int """ @@ -528,7 +528,7 @@ class file: the reference to the deleted will be removed from the aggregate. :param inst: The entity instance to delete - :type inst: ifcopenshell.entity_instance.entity_instance + :type inst: ifcopenshell.entity_instance :rtype: None """ if self.transaction: diff --git a/src/ifcopenshell-python/ifcopenshell/util/constraint.py b/src/ifcopenshell-python/ifcopenshell/util/constraint.py index b8f6aac14f..f4e18d61b3 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/util/constraint.py @@ -27,9 +27,9 @@ def get_constraints(product: ifcopenshell.entity_instance) -> list[ifcopenshell. Retrieves the constraints assigned to the `product`. :param product: The IFC element. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :return: List of assigned constraints. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ constraints = [] for rel in product.HasAssociations or []: @@ -43,9 +43,9 @@ def get_constrained_elements(constraint: ifcopenshell.entity_instance) -> set[if Retrieves the elements constrained by a `constraint`. :param product: The IFC element. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :return: Set of elements constrained by a `constrant`. - :rtype: set[ifcopenshell.entity_instance.entity_instance] + :rtype: set[ifcopenshell.entity_instance] """ elements = set() for rel in constraint.file.get_inverse(constraint): @@ -59,9 +59,9 @@ def get_metrics(constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.e Retrieves the list of nested constraints for a IfcObjective `constraint`. :param product: IfcObjective constraint. - :type product: ifcopenshell.entity_instance.entity_instance + :type product: ifcopenshell.entity_instance :return: List of nested constraints. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ metrics = [] diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index bb23d1c95a..70c110a63d 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -40,7 +40,7 @@ def get_pset( occurrence, not the type's pset. :param element: The IFC Element entity - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param name: The name of the pset :type name: str :param prop: The name of the property @@ -128,7 +128,7 @@ def get_psets( occurrence, not the type's pset. :param element: The IFC Element entity - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param psets_only: Default as False. Set to true if only property sets are needed. :type psets_only: bool,optional :param qtos_only: Default as False. Set to true if only quantities are needed. @@ -418,7 +418,7 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str: considered first. :param element: The IFC Element entity - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The predefined type of the element :rtype: str @@ -448,9 +448,9 @@ def get_type(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_insta """Retrieves the construction type element of an element occurrence :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :return: The related type element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: @@ -473,9 +473,9 @@ def get_types(type: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_in """Get all the occurrences of a type element :param type: The type element - :type type: ifcopenshell.entity_instance.entity_instance + :type type: ifcopenshell.entity_instance :return: A list of occurrences of that type - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -495,9 +495,9 @@ def get_shape_aspects(element: ifcopenshell.entity_instance) -> list[ifcopenshel """Gets element shape aspects :param element: The element to get the shape aspects of. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The associated shape aspects of the element. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -530,7 +530,7 @@ def get_material( constituent), or a material set usage. :param element: The element to get the material of. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param should_skip_usage: If set to True, if the material is a material set usage, the material set itself will be returned. Useful if you don't care about occurrence usage parameters. If False, the usage will be @@ -540,7 +540,7 @@ def get_material( types will be considered. :type should_inherit: bool :return: The associated material of the element or `None`. - :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] + :rtype: Union[ifcopenshell.entity_instance, None] Example: @@ -574,11 +574,11 @@ def get_materials( returned as a list. :param element: The element to get the materials of. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param should_inherit: If True, any inherited materials from associated types will be considered. :return: The associated materials of the element. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -608,9 +608,9 @@ def get_styles(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entit Styles may be retreived from the material or the body representation. :param element: The element to get the styles of. - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: A list of surface styles - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -655,11 +655,11 @@ def get_elements_by_material( usage. :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param material: The IFC Material entity - :type material: ifcopenshell.entity_instance.entity_instance + :type material: ifcopenshell.entity_instance :return: A list of elements using the to the material - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -696,11 +696,11 @@ def get_elements_by_style( """Retrieves the elements whose geometric representation uses a style :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param style: The IfcPresentationStyle entity - :type style: ifcopenshell.entity_instance.entity_instance + :type style: ifcopenshell.entity_instance :return: The elements related to the style - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -738,11 +738,11 @@ def get_elements_by_representation( """Gets all elements using a geometric representation :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param representation: The IfcShapeRepresentation representation - :type representation: ifcopenshell.entity_instance.entity_instance + :type representation: ifcopenshell.entity_instance :return: The elements using the geometric representation - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -772,11 +772,11 @@ def get_elements_by_layer( """Get all the elements that are used by a presentation layer :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param layer: The IfcPresentationLayerAssignment layer - :type layer: ifcopenshell.entity_instance.entity_instance + :type layer: ifcopenshell.entity_instance :return: The elements using the geometric representation - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ results = set() for item in layer.AssignedItems or []: @@ -798,11 +798,11 @@ def get_layers( traditional CAD presentation layer. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param element: The IFC element to interrogate - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: A list of IfcPresentationLayerAssignment - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -836,7 +836,7 @@ def get_container( Retrieves the spatial structure container of an element. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param should_get_direct: If True, a result is only returned if the element is directly contained in a spatial structure element. If False, an indirect spatial container may be returned, such as if an element is a @@ -847,7 +847,7 @@ def get_container( example, you may be after the storey, not a space. :type ifc_class: str, optional :return: The direct or indirect container of the element or None. - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: @@ -892,9 +892,9 @@ def get_referenced_structures(element: ifcopenshell.entity_instance) -> list[ifc as stairs, doors, etc. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: A list of IfcSpatialElement - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -910,9 +910,9 @@ def get_structure_referenced_elements(structure: ifcopenshell.entity_instance) - """Retreives a set of elements referenced by a structure :param structure: IfcSpatialElement - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: A set of referenced elements, IfcSpatialReferenceSelect - :rtype: set[ifcopenshell.entity_instance.entity_instance] + :rtype: set[ifcopenshell.entity_instance] Example: @@ -934,9 +934,9 @@ def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) parts of an aggreate, all openings, and all fills of any openings. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The decomposition of the element - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -978,9 +978,9 @@ def get_grouped_by(element: ifcopenshell.entity_instance) -> list[ifcopenshell.e """Retrieves all subelements of an element based on the group. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: All subelements of the group - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -1006,7 +1006,7 @@ def get_groups(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entit :param element: The IFC element :return: List of IfcGroups element is assigned to. - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -1027,9 +1027,9 @@ def get_aggregate(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_ Retrieves the aggregate parent of an element. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The aggregate of the element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: @@ -1048,9 +1048,9 @@ def get_nest(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_insta Retrieves the nest parent of an element. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The nested whole of the element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance Example: @@ -1072,9 +1072,9 @@ def get_parts(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity Retrieves the parts of an element that have an aggregation relationship. :param element: The IFC element - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The parts of the element - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -1098,7 +1098,7 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) - :param include_ports: Default as False. Set to true if you also want to get ports. :type include_ports: bool,optional :return: The components of the element - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] Example: @@ -1141,9 +1141,9 @@ def get_referenced_elements(reference: ifcopenshell.entity_instance) -> set[ifco """Get all elements with assigned `reference` :param reference: IfcExternalReference subtype reference - :type reference: ifcopenshell.entity_instance.entity_instance + :type reference: ifcopenshell.entity_instance :return: The elements with assigned `reference` - :rtype: set[ifcopenshell.entity_instance.entity_instance] + :rtype: set[ifcopenshell.entity_instance] Example: @@ -1222,7 +1222,7 @@ def batch_remove_deep2(ifc_file: ifcopenshell.file) -> None: on existing variables in memory. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :rtype: None Example: @@ -1249,9 +1249,9 @@ def unbatch_remove_deep2(ifc_file: ifcopenshell.file) -> ifcopenshell.file: See documentation for batch_remove_deep2. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :return: A newly loaded file with the elements removed. - :rtype: ifcopenshell.file.file + :rtype: ifcopenshell.file """ ifc_string = ifc_file.to_string() lines = iter(ifc_string.split("\n")) @@ -1304,13 +1304,13 @@ def remove_deep2( subgraph but are protected from deletion. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param also_consider: elements to also consider as a part of a subgraph - :type also_consider: list[ifcopenshell.entity_instance.entity_instance], optional + :type also_consider: list[ifcopenshell.entity_instance], optional :param do_not_delete: elements to protect from deletion - :type do_not_delete: list[ifcopenshell.entity_instance.entity_instance], optional + :type do_not_delete: list[ifcopenshell.entity_instance], optional :param element: The starting element that defines the subgraph - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance """ # ifc_file.batch() to_delete = set() @@ -1357,11 +1357,11 @@ def copy(ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> GlobalIds are regenerated. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param element: The IFC element to copy - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :return: The newly copied element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ new = ifc_file.create_entity(element.is_a()) for i, attribute in enumerate(element): @@ -1387,9 +1387,9 @@ def copy_deep( GlobalIds are regenerated. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param element: The IFC element to copy - :type element: ifcopenshell.entity_instance.entity_instance + :type element: ifcopenshell.entity_instance :param exclude: An optional list of strings of IFC class names to not copy. If any of the subelement is this class, it will not be copied and the original instance will be referenced. @@ -1400,9 +1400,9 @@ def copy_deep( :param copied_entities: A dictionary of IDs as keys and entities as values to reuse when coming across the same entity twice. This can typically be left as None. - :type copied_entities: dict[int:ifcopenshell.entity_instance.entity_instance], optional + :type copied_entities: dict[int:ifcopenshell.entity_instance], optional :return: The newly copied element - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ if copied_entities is None: copied_entities = {} diff --git a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py index 7128630c2f..ffe2d9228f 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/geolocation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/geolocation.py @@ -147,7 +147,7 @@ def auto_xyz2enh(ifc_file, x, y, z): https://www.buildingsmart.org/standards/bsi-standards/standards-library/ :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param x: The X local engineering coordinate provided in project length units. :type x: float :param y: The Y local engineering coordinate provided in project length units. @@ -215,7 +215,7 @@ def auto_enh2xyz(ifc_file, easting, northing, height): https://www.buildingsmart.org/standards/bsi-standards/standards-library/ :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param easting: The global easting map coordinate provided in map units. :type easting: float :param northing: The global northing map coordinate provided in map units. @@ -283,7 +283,7 @@ def auto_z2e(ifc_file, z): https://www.buildingsmart.org/standards/bsi-standards/standards-library/ :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param z: The Z local engineering coordinate provided in project length units. :type z: float :return: The elevation in project length units. @@ -587,7 +587,7 @@ def get_grid_north(ifc_file): https://www.buildingsmart.org/standards/bsi-standards/standards-library/ :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :return: An angle to grid north in decimal degrees :rtype: float """ @@ -623,7 +623,7 @@ def get_true_north(ifc_file): instead. :param ifc_file: The IFC file - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :return: An angle to true north in decimal degrees :rtype: float """ diff --git a/src/ifcopenshell-python/ifcopenshell/util/placement.py b/src/ifcopenshell-python/ifcopenshell/util/placement.py index a5c3312dab..75a1de6265 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/placement.py +++ b/src/ifcopenshell-python/ifcopenshell/util/placement.py @@ -60,7 +60,7 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> MatrixType: should use ``get_local_placement`` instead. :param placement: The IfcLocalPlacement enitity - :type placement: ifcopenshell.entity_instance.entity_instance + :type placement: ifcopenshell.entity_instance :return: A 4x4 numpy matrix :rtype: MatrixType """ @@ -118,7 +118,7 @@ def get_local_placement(placement: ifcopenshell.entity_instance) -> MatrixType: matrix = ifcopenshell.util.placement.get_local_placement(placement) :param placement: The IfcLocalPlacement entity - :type placement: ifcopenshell.entity_instance.entity_instance + :type placement: ifcopenshell.entity_instance :return: A 4x4 numpy matrix :rtype: MatrixType """ @@ -138,7 +138,7 @@ def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) -> ``get_mappeditem_transformation`` instead. :param item: The IfcCartesianTransformationOperator entity - :type item: ifcopenshell.entity_instance.entity_instance + :type item: ifcopenshell.entity_instance :return: A 4x4 numpy transformation matrix :rtype: MatrixType """ @@ -184,7 +184,7 @@ def get_mappeditem_transformation(item: ifcopenshell.entity_instance) -> MatrixT transformation matrix. :param item: The IfcMappedItem entity - :type item: ifcopenshell.entity_instance.entity_instance + :type item: ifcopenshell.entity_instance :return: A 4x4 numpy transformation matrix :rtype: MatrixType """ @@ -201,7 +201,7 @@ def get_storey_elevation(storey: ifcopenshell.entity_instance) -> float: its placement, or as a fallback the ``Elevation`` attribute. :param storey: The IfcBuildingStorey entity - :type storey: ifcopenshell.entity_instance.entity_instance + :type storey: ifcopenshell.entity_instance :return: The elevation in project units :rtype: float """ diff --git a/src/ifcopenshell-python/ifcopenshell/util/representation.py b/src/ifcopenshell-python/ifcopenshell/util/representation.py index 95e91490d0..9bbfc783c4 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/representation.py +++ b/src/ifcopenshell-python/ifcopenshell/util/representation.py @@ -88,9 +88,9 @@ def resolve_representation(representation: ifcopenshell.entity_instance) -> ifco """Resolve possibly mapped representation. :param representation: IfcRepresentation - :type representation: ifcopenshell.entity_instance.entity_instance + :type representation: ifcopenshell.entity_instance :return: Representation resolved from mappings - :rtype: ifcopenshell.entity_instance.entity_instance + :rtype: ifcopenshell.entity_instance """ if len(representation.Items) == 1 and representation.Items[0].is_a("IfcMappedItem"): return resolve_representation(representation.Items[0].MappingSource.MappedRepresentation) diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 15ba9e0762..d713a331c4 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -287,17 +287,17 @@ def filter_elements( Filter elements based on the provided `query`. :param ifc_file: The IFC file object - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param query: Query to execute :type query: str :param elements: Base set of IFC elements for the query. If provided, new elements found for the current query will be added to `elements`. Elements explicitly excluded in the `query` will also be excluded from `elements` - :type elements: set[ifcopenshell.entity_instance.entity_instance], optional + :type elements: set[ifcopenshell.entity_instance], optional :param edit_in_place: If `True`, mutate the provided `elements` in place. Defaults to `False` :type edit_in_place: bool :return: Set of filtered elements - :rtype: set[ifcopenshell.entity_instance.entity_instance] + :rtype: set[ifcopenshell.entity_instance] Example: diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape.py b/src/ifcopenshell-python/ifcopenshell/util/shape.py index 931f4c0327..1915c6cde0 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape.py @@ -161,7 +161,7 @@ def get_element_bbox_centroid(element: ifcopenshell.entity_instance, geometry) - is more efficient to use ``get_shape_bbox_centroid``. :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :param geometry: Geometry output calculated by IfcOpenShell :type geometry: geometry :return: A tuple representing the XYZ centroid @@ -271,7 +271,7 @@ def get_element_vertices(element: ifcopenshell.entity_instance, geometry) -> npt Results are a nested numpy array e.g. [[v1x, v1y, v1z], [v2x, v2y, v2z], ...] :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :param geometry: Geometry output calculated by IfcOpenShell :type geometry: geometry :return: A numpy array listing all the vertices. Each vertex is a numpy array with XYZ coordinates. @@ -347,7 +347,7 @@ def get_element_bottom_elevation(element: ifcopenshell.entity_instance, geometry ``get_shape_bottom_elevation``. :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :param geometry: Geometry output calculated by IfcOpenShell :type geometry: geometry :return: The Z value @@ -363,7 +363,7 @@ def get_element_top_elevation(element: ifcopenshell.entity_instance, geometry) - ``get_shape_top_elevation``. :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :param geometry: Geometry output calculated by IfcOpenShell :type geometry: geometry :return: The Z value @@ -656,9 +656,9 @@ def get_profiles(element: ifcopenshell.entity_instance) -> list[ifcopenshell.ent solid extrusions. This is useful for later doing 2D take-off from profiles. :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :return: A list of profiles - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ material = ifcopenshell.util.element.get_material(element, should_skip_usage=True) if material and material.is_a("IfcMaterialProfileSet"): @@ -670,9 +670,9 @@ def get_extrusions(element: ifcopenshell.entity_instance) -> list[ifcopenshell.e """Gets all extruded area solids used to define an element's model body geometry :param element: The element occurrence - :type: ifcopenshell.entity_instance.entity_instance + :type: ifcopenshell.entity_instance :return: A list of extrusion representation items - :rtype: list[ifcopenshell.entity_instance.entity_instance] + :rtype: list[ifcopenshell.entity_instance] """ representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if not representation: diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 0e2604e481..fdbee952a4 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -398,7 +398,7 @@ def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcop """Get the default project unit of a particular unit type :param ifc_file: The IFC file. - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param unit_type: The type of unit, taken from the list of IFC unit types, such as "LENGTHUNIT". :type unit_type: str @@ -536,9 +536,9 @@ def convert_unit(value: float, from_unit: ifcopenshell.entity_instance, to_unit: :param value: The numeric value you want to convert :type value: float :param from_unit: The IfcNamedUnit to confirm from. - :type from_unit: ifcopenshell.entity_instance.entity_instance + :type from_unit: ifcopenshell.entity_instance :param to_unit: The IfcNamedUnit to confirm from. - :type to_unit: ifcopenshell.entity_instance.entity_instance + :type to_unit: ifcopenshell.entity_instance :return: The converted value. :rtype: float """ @@ -599,7 +599,7 @@ def calculate_unit_scale(ifc_file: ifcopenshell.file, unit_type: str = "LENGTHUN si_meters / unit_scale = ifc_project_length :param ifc_file: The IFC file. - :type ifc_file: ifcopenshell.file.file + :type ifc_file: ifcopenshell.file :param unit_type: The type of SI unit, defaults to "LENGTHUNIT" :type unit_type: str :returns: The scale factor From 5da4fbb39cf92ca7320cb4a6d9c1878913fa8ed2 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 7 May 2024 23:36:56 +1000 Subject: [PATCH 57/62] Fix #4631. Fix packaging problem on PyPI for IfcPatch. --- src/ifcpatch/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcpatch/pyproject.toml b/src/ifcpatch/pyproject.toml index 7607cf6a00..25c9bb982c 100644 --- a/src/ifcpatch/pyproject.toml +++ b/src/ifcpatch/pyproject.toml @@ -23,5 +23,5 @@ Documentation = "https://docs.ifcopenshell.org" Issues = "https://github.com/IfcOpenShell/IfcOpenShell/issues" [tool.setuptools.packages.find] -include = ["ifcpatch"] +include = ["ifcpatch*"] exclude = ["test*"] From c5b4513f659cc16c6cf19db4e3c8776e3e9e33ed Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Tue, 7 May 2024 09:46:55 -0500 Subject: [PATCH 58/62] fix #4622 - can now reassign IfcWindowStyle and IfcDoorStyle --- src/blenderbim/blenderbim/bim/module/root/data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/root/data.py b/src/blenderbim/blenderbim/bim/module/root/data.py index 18f7fbffcd..9f9d3a6f91 100644 --- a/src/blenderbim/blenderbim/bim/module/root/data.py +++ b/src/blenderbim/blenderbim/bim/module/root/data.py @@ -185,6 +185,8 @@ class IfcClassData: if element: if element.is_a("IfcOpeningElement") or element.is_a("IfcOpeningStandardCase"): return False + if element.is_a() in ("IfcWindowStyle", "IfcDoorStyle"): #see https://github.com/IfcOpenShell/IfcOpenShell/issues/4622#issuecomment-2095676368 + return True for product in cls.ifc_products(): if element.is_a(product[0]): return True From 267527f3fcea1b1a528e5a70ce7dd4c63a195f20 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 7 May 2024 21:26:48 +0500 Subject: [PATCH 59/62] fix issue after removing ifcopenshell.main in d76462ca4 --- src/ifcopenshell-python/ifcopenshell/template.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/template.py b/src/ifcopenshell-python/ifcopenshell/template.py index 7e506e3fd6..13942431d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/template.py +++ b/src/ifcopenshell-python/ifcopenshell/template.py @@ -22,7 +22,7 @@ import uuid from .file import file from .guid import compress -from . import main +from .ifcopenshell_wrapper import version # A quick way to setup an 'empty' IFC file, taken from: # http://academy.ifcopenshell.org/creating-a-simple-wall-with-property-set-and-quantity-information/ @@ -58,8 +58,8 @@ END-ISO-10303-21; """ DEFAULTS = { - "application": lambda d: "IfcOpenShell-%s" % main.version, - "application_version": lambda d: main.version, + "application": lambda d: "IfcOpenShell-%s" % version(), + "application_version": lambda d: version(), "project_globalid": lambda d: compress(uuid.uuid4().hex), "schema_identifier": lambda d: "IFC4", "timestamp": lambda d: int(time.time()), From b8275f280268713cfec03f8a733ff4daa84d15c4 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 7 May 2024 21:39:06 +0500 Subject: [PATCH 60/62] fix errors using deprecated api after ab696b9 #4632 --- src/ifcopenshell-python/ifcopenshell/api/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index a4e99ad318..2e6e2c1370 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -158,9 +158,6 @@ def run( for listener in pre_listeners.get(usecase_path, {}).values(): listener(usecase_path, ifc_file, settings) - # see #4531 - if usecase_path in ARGUMENTS_DEPRECATION: - usecase_path, settings = ARGUMENTS_DEPRECATION[usecase_path](usecase_path, settings) # TODO: settings serialization for client-server systems # def serialise_entity_instance(entity): @@ -304,10 +301,15 @@ def wrap_usecase(usecase_path, usecase): def wrapper(*args, should_run_listeners: bool = True, **settings): ifc_file = args[0] if args else None + nonlocal usecase_path if should_run_listeners: for listener in pre_listeners.get(usecase_path, {}).values(): listener(usecase_path, ifc_file, settings) + # see #4531 + if usecase_path in ARGUMENTS_DEPRECATION: + usecase_path, settings = ARGUMENTS_DEPRECATION[usecase_path](usecase_path, settings) + try: result = usecase(*args, **settings) except TypeError as e: From bc72c927c1d6737730a2db1391e5b4a9e4de21d3 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 7 May 2024 21:41:01 +0500 Subject: [PATCH 61/62] replace deprecated api call noticed fixing #4632 --- src/blenderbim/blenderbim/bim/module/geometry/operator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 312ceed51a..aacb218c2e 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -926,7 +926,7 @@ class OverrideDuplicateMove(bpy.types.Operator): if r.is_a("IfcRelAssignsToGroup") if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name ] - tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], product=new[0]) + tool.Ifc.run("group.unassign_group", group=linked_aggregate_group[0], products=[new[0]]) class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro): From 984b1212a6a394e7f39c37fc7294d2ef8e0d42fc Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 8 May 2024 09:45:05 +1000 Subject: [PATCH 62/62] Whoops --- src/blenderbim/docs/devs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blenderbim/docs/devs/installation.rst b/src/blenderbim/docs/devs/installation.rst index d3fe796316..20e9a3c6e1 100644 --- a/src/blenderbim/docs/devs/installation.rst +++ b/src/blenderbim/docs/devs/installation.rst @@ -160,7 +160,7 @@ Before running it follow the instructions descibed after `rem` tags. mklink /D "%blenderbim%\bim" "%cd%\src\blenderbim\blenderbim\bim" echo Copy over compiled IfcOpenShell files... - copy %blenderbim%\libs\site\packages\ifcopenshell\*_wrapper* %cd%\src\ifcopenshell-python\ifcopenshell\ + copy "%blenderbim%\libs\site\packages\ifcopenshell\*_wrapper*" "%cd%\src\ifcopenshell-python\ifcopenshell\" echo Remove the IfcOpenShell dependency... rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell"