diff --git a/src/bonsai/bonsai/bim/module/boundary/operator.py b/src/bonsai/bonsai/bim/module/boundary/operator.py index e3bc51683c..5d83a88597 100644 --- a/src/bonsai/bonsai/bim/module/boundary/operator.py +++ b/src/bonsai/bonsai/bim/module/boundary/operator.py @@ -25,6 +25,7 @@ import mathutils import numpy as np import multiprocessing import ifcopenshell.api +import ifcopenshell.api.boundary import ifcopenshell.geom import ifcopenshell.util.unit import ifcopenshell.util.shape @@ -416,7 +417,7 @@ class UpdateBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator): def _execute(self, context): tool.Boundary.move_origin_to_space_origin(context.active_object) settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object) - ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings) + ifcopenshell.api.boundary.assign_connection_geometry(tool.Ifc.get(), **settings) return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index de59b7a61c..6a234800bf 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -29,6 +29,7 @@ import ifcopenshell.util.representation import ifcopenshell.util.shape_builder import ifcopenshell.util.unit import ifcopenshell.api +import ifcopenshell.api.boundary import ifcopenshell.api.grid import bonsai.core.geometry import bonsai.core.geometry as core @@ -479,7 +480,7 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator): elif product.is_a("IfcRelSpaceBoundary"): # TODO refactor settings = tool.Boundary.get_assign_connection_geometry_settings(obj) - ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings) + ifcopenshell.api.boundary.assign_connection_geometry(tool.Ifc.get(), **settings) return if tool.Ifc.is_moved(obj) or tool.Geometry.is_scaled(obj): diff --git a/src/bonsai/bonsai/bim/module/profile/operator.py b/src/bonsai/bonsai/bim/module/profile/operator.py index b0d5073429..1a330917db 100644 --- a/src/bonsai/bonsai/bim/module/profile/operator.py +++ b/src/bonsai/bonsai/bim/module/profile/operator.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell.api +import ifcopenshell.api.profile import ifcopenshell.util.element import bonsai.bim.helper import bonsai.tool as tool @@ -174,12 +175,12 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator): props.object_to_profile = None if not indices: points = [(0, 0), (0.1, 0), (0.1, 0.1), (0, 0.1), (0, 0)] - profile = ifcopenshell.api.run("profile.add_arbitrary_profile", tool.Ifc.get(), profile=points) + profile = ifcopenshell.api.profile.add_arbitrary_profile(tool.Ifc.get(), profile=points) else: if "inner_curves" not in indices: points = [(obj.data.vertices[i].co.x, obj.data.vertices[i].co.y) for i in indices["profile"]] points.append(points[0]) - profile = ifcopenshell.api.run("profile.add_arbitrary_profile", tool.Ifc.get(), profile=points) + profile = ifcopenshell.api.profile.add_arbitrary_profile(tool.Ifc.get(), profile=points) else: outer_points = [(obj.data.vertices[i].co.x, obj.data.vertices[i].co.y) for i in indices["profile"]] outer_points.append(outer_points[0]) @@ -189,8 +190,7 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator): ] for curve in inner_points: curve.append(curve[0]) - profile = ifcopenshell.api.run( - "profile.add_arbitrary_profile_with_voids", + profile = ifcopenshell.api.profile.add_arbitrary_profile_with_voids( tool.Ifc.get(), outer_profile=outer_points, inner_profiles=inner_points, diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py index 3cbab7f38d..ff0e2b11b7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py @@ -39,11 +39,8 @@ def edit_attributes(file: ifcopenshell.file, product: ifcopenshell.entity_instan :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 :return: None - :rtype: None Example: 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 d77776f04a..b9a2630442 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py @@ -17,17 +17,20 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.unit +import numpy as np +import numpy.typing as npt from typing import Optional +from ifcopenshell.util.shape_builder import SequenceOfVectors, V, ifc_safe_vector_type def assign_connection_geometry( file: ifcopenshell.file, rel_space_boundary: ifcopenshell.entity_instance, - outer_boundary: list[tuple[float, float]], + outer_boundary: SequenceOfVectors, location: tuple[float, float, float], axis: tuple[float, float, float], ref_direction: tuple[float, float, float], - inner_boundaries: Optional[list[list[tuple[float, float]]]] = None, + inner_boundaries: Optional[SequenceOfVectors] = None, unit_scale: Optional[float] = None, ) -> None: """Create and assign a connection geometry to a space boundary relationship @@ -40,35 +43,28 @@ def assign_connection_geometry( :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[tuple[float, 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[tuple[float, 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: tuple[float, float, 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: tuple[float, float, 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: tuple[float, float, 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: @@ -83,19 +79,26 @@ def assign_connection_geometry( 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.outer_boundary = V(outer_boundary) + usecase.inner_boundaries = V(inner_boundaries or []) + usecase.location = V(location) + usecase.axis = V(axis) + usecase.ref_direction = V(ref_direction) + usecase.unit_scale = unit_scale if unit_scale is not None else ifcopenshell.util.unit.calculate_unit_scale(file) return usecase.execute() class Usecase: + file: ifcopenshell.file + rel_space_boundary: ifcopenshell.entity_instance + outer_boundary: npt.NDArray + inner_boundaries: npt.NDArray + location: npt.NDArray + axis: npt.NDArray + ref_direction: npt.NDArray + unit_scale: float + def execute(self): - if self.unit_scale is None: - self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) outer_boundary = self.create_polyline(self.outer_boundary) inner_boundaries = tuple(self.create_polyline(boundary) for boundary in self.inner_boundaries) plane = self.create_plane(self.location, self.axis, self.ref_direction) @@ -103,18 +106,23 @@ class Usecase: connection_geometry = self.file.createIfcConnectionSurfaceGeometry(curve_bounded_plane) self.rel_space_boundary.ConnectionGeometry = connection_geometry - def create_point(self, point): - return self.file.createIfcCartesianPoint(point / self.unit_scale) + def create_point(self, point: npt.NDArray) -> ifcopenshell.entity_instance: + return self.file.create_enitty("IfcCartesianPoint", ifc_safe_vector_type(point / self.unit_scale)) - def close_polyline(self, points): + def close_polyline( + self, points: tuple[ifcopenshell.entity_instance, ...] + ) -> tuple[ifcopenshell.entity_instance, ...]: return points + (points[0],) - def create_polyline(self, points): - if points[0] == points[-1]: + def create_polyline(self, points: npt.NDArray) -> ifcopenshell.entity_instance: + if np.allclose(points[0], points[-1]): points = points[0 : len(points) - 1] - return self.file.createIfcPolyline(self.close_polyline(tuple(self.create_point(point) for point in points))) + ifc_points = tuple(self.create_point(point) for point in points) + return self.file.createIfcPolyline(self.close_polyline(ifc_points)) - def create_plane(self, location, axis, ref_direction): + def create_plane( + self, location: npt.NDArray, axis: npt.NDArray, ref_direction: npt.NDArray + ) -> ifcopenshell.entity_instance: return self.file.createIfcPlane( self.file.createIfcAxis2Placement3D( self.create_point(location), diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py index af886a0014..2932a5bb93 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py @@ -20,7 +20,7 @@ import ifcopenshell import ifcopenshell.guid import ifcopenshell.util.schema import ifcopenshell.util.date -from typing import Union +from typing import Union, Any def add_classification( @@ -61,9 +61,7 @@ def add_classification( 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: @@ -81,28 +79,28 @@ def add_classification( """ usecase = Usecase() usecase.file = file - usecase.settings = { - "classification": classification, - } - return usecase.execute() + return usecase.execute(classification) class Usecase: - def execute(self): - if isinstance(self.settings["classification"], str): - classification = self.file.createIfcClassification(Name=self.settings["classification"]) + file: ifcopenshell.file + + def execute(self, classification: Union[str, ifcopenshell.entity_instance]) -> ifcopenshell.entity_instance: + self.classification = classification + if isinstance(self.classification, str): + classification = self.file.create_entity("IfcClassification", Name=self.classification) self.relate_to_project(classification) return classification return self.add_from_library() - def add_from_library(self): + def add_from_library(self) -> ifcopenshell.entity_instance: edition_date = None - if self.settings["classification"].EditionDate: - edition_date = ifcopenshell.util.date.ifc2datetime(self.settings["classification"].EditionDate) - self.settings["classification"].EditionDate = None + if self.classification.EditionDate: + edition_date = ifcopenshell.util.date.ifc2datetime(self.classification.EditionDate) + self.classification.EditionDate = None migrator = ifcopenshell.util.schema.Migrator() - result = migrator.migrate(self.settings["classification"], self.file) + result = migrator.migrate(self.classification, self.file) # TODO: should auto date migration be part of the migrator? if self.file.schema == "IFC2X3" and edition_date: @@ -118,7 +116,7 @@ class Usecase: return result - def relate_to_project(self, classification): + def relate_to_project(self, classification: ifcopenshell.entity_instance) -> None: self.file.create_entity( "IfcRelAssociatesClassification", GlobalId=ifcopenshell.guid.new(), diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index 1e0ae329bf..817d15b5cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -21,7 +21,7 @@ import ifcopenshell.api.owner import ifcopenshell.guid import ifcopenshell.util.element import ifcopenshell.util.schema -from typing import Optional, Union +from typing import Optional, Union, Any def add_reference( @@ -65,23 +65,18 @@ def add_reference( :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 @@ -91,13 +86,11 @@ def add_reference( 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: @@ -136,6 +129,9 @@ def add_reference( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 d6c8ef8bc4..b2091c0eca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py @@ -28,11 +28,8 @@ def edit_classification( 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 :return: None - :rtype: None Example: @@ -43,7 +40,5 @@ def edit_classification( ifcopenshell.api.classification.edit_classification(model, classification=classification, attributes={"Name": "Foo"}) """ - settings = {"classification": classification, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["classification"], name, value) + for name, value in attributes.items(): + setattr(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 80d978e7d3..f5a85d40f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py @@ -28,11 +28,8 @@ def edit_reference( 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 :return: None - :rtype: None Example: @@ -43,7 +40,5 @@ def edit_reference( ifcopenshell.api.classification.edit_reference(model, reference=reference, attributes={"Name": "Foo"}) """ - settings = {"reference": reference, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["reference"], name, value) + for name, value in attributes.items(): + setattr(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 90e6abbbe7..38382eda91 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -28,9 +28,7 @@ def remove_classification(file: ifcopenshell.file, classification: ifcopenshell. removed from a project. :param classification: The IfcClassification entity you want to remove - :type classification: ifcopenshell.entity_instance :return: None - :rtype: None Example: @@ -42,16 +40,17 @@ def remove_classification(file: ifcopenshell.file, classification: ifcopenshell. """ usecase = Usecase() usecase.file = file - usecase.settings = {"classification": classification} - return usecase.execute() + return usecase.execute(classification) class Usecase: - def execute(self): - references = self.get_references(self.settings["classification"]) + file: ifcopenshell.file + + def execute(self, classification: ifcopenshell.entity_instance) -> None: + references = self.get_references(classification) for reference in references: self.file.remove(reference) - self.file.remove(self.settings["classification"]) + self.file.remove(classification) for rel in self.file.by_type("IfcRelAssociatesClassification"): if not rel.RelatingClassification: history = rel.OwnerHistory diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py index 0bdd347bfa..55dfd11b08 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py @@ -33,15 +33,12 @@ def remove_reference( :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. :return: None - :rtype: None Example: @@ -56,20 +53,18 @@ def remove_reference( ifcopenshell.api.classification.remove_reference(model, reference=reference, products=[wall_type]) """ - settings = {"reference": reference, "products": products} - is_ifc2x3 = file.schema == "IFC2X3" - products = set(settings["products"]) - referenced = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) - products -= products.difference(referenced) + products_set = set(products) + referenced = ifcopenshell.util.element.get_referenced_elements(reference) + products_set -= products_set.difference(referenced) # all products are already unassigned from a reference - if not products: + if not products_set: return rooted_products: set[ifcopenshell.entity_instance] = set() non_rooted_products: set[ifcopenshell.entity_instance] = set() - for product in settings["products"]: + for product in products: if product.is_a("IfcRoot"): rooted_products.add(product) else: @@ -86,7 +81,7 @@ def remove_reference( reference_rels = { rel for rel in reference_rels - if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == settings["reference"] + if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == reference } for rel in reference_rels: @@ -108,7 +103,7 @@ def remove_reference( rels = getattr(product, "HasExternalReference", []) reference_rels.update(rels) - reference_rels = {rel for rel in reference_rels if rel.RelatingReference == settings["reference"]} + reference_rels = {rel for rel in reference_rels if rel.RelatingReference == reference} for rel in reference_rels: related_objects = set(rel.RelatedResourceObjects) - non_rooted_products if related_objects: @@ -117,6 +112,6 @@ def remove_reference( file.remove(rel) # TODO: we only handle lightweight classifications here - referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + referenced_elements = ifcopenshell.util.element.get_referenced_elements(reference) if not referenced_elements: - file.remove(settings["reference"]) + file.remove(reference) 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 fdcdf50f6f..63ecfeb3c0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py @@ -26,16 +26,14 @@ def add_metric_reference( 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} - references_created = [] - if settings["reference_path"]: - attributes = settings["reference_path"].split(".") + if reference_path: + attributes = reference_path.split(".") for i in range(len(attributes)): if i == 0: reference = file.create_entity("IfcReference") reference.AttributeIdentifier = attributes[i] - settings["metric"].ReferencePath = reference + metric.ReferencePath = reference references_created.append(reference) else: reference = file.create_entity("IfcReference") diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py index 74bf3d9a04..f4daad0abb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py @@ -39,36 +39,29 @@ def assign_constraint( :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() + return usecase.execute(products, constraint) class Usecase: - def execute(self): - products = set(self.settings["products"]) + file: ifcopenshell.file + + def execute(self, products: list[ifcopenshell.entity_instance], constraint: ifcopenshell.entity_instance): if not products: return + products_set = set(products) - self.constraint = self.settings["constraint"] - - rels = self.get_constraint_rels() + rels = self.get_constraint_rels(constraint) related_objects = set() for rel in rels: related_objects.update(rel.RelatedObjects) - products_to_assign = products - related_objects + products_to_assign = products_set - related_objects if not products_to_assign: return rels[0] @@ -85,14 +78,14 @@ class Usecase: **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(self.file), - "RelatingConstraint": self.constraint, + "RelatingConstraint": constraint, "RelatedObjects": list(products_to_assign), } ) - def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]: + def get_constraint_rels(self, constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: rels = [] - for rel in self.file.get_inverse(self.constraint): + for rel in self.file.get_inverse(constraint): if rel.is_a("IfcRelAssociatesConstraint"): rels.append(rel) return rels diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py index 89a0cbef7d..3967846759 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py @@ -26,11 +26,8 @@ def edit_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance, a 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance, a ifcopenshell.api.constraint.edit_metric(model, metric=metric, attributes={"ConstraintGrade": "HARD"}) """ - settings = {"metric": metric, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["metric"], name, value) + for name, value in attributes.items(): + setattr(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 ce4e76ba9e..3cd75356a7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py @@ -28,11 +28,8 @@ def edit_objective( 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_objective( ifcopenshell.api.constraint.edit_objective(model, objective=objective, attributes={"ConstraintGrade": "HARD"}) """ - settings = {"objective": objective, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["objective"], name, value) + for name, value in attributes.items(): + setattr(objective, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py index f700a2c150..76668f714d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.util.element def remove_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance) -> None: @@ -41,17 +42,18 @@ def remove_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance) """ usecase = Usecase() usecase.file = file - usecase.settings = {"metric": metric} - return usecase.execute() + return usecase.execute(metric) class Usecase: - def execute(self): - if self.settings["metric"].ReferencePath: - reference = self.settings["metric"].ReferencePath + file: ifcopenshell.file + + def execute(self, metric: ifcopenshell.entity_instance) -> None: + if metric.ReferencePath: + reference = metric.ReferencePath self.delete_reference(reference) - self.file.remove(self.settings["metric"]) + self.file.remove(metric) for rel in self.file.by_type("IfcRelAssociatesConstraint"): if not rel.RelatingConstraint: history = rel.OwnerHistory @@ -62,7 +64,7 @@ class Usecase: if not resource_rel.RelatingConstraint: self.file.remove(resource_rel) - def delete_reference(self, reference): + def delete_reference(self, reference: ifcopenshell.entity_instance) -> None: if reference.InnerReference: self.delete_reference(reference.InnerReference) self.file.remove(reference) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py index cff9716809..d9e84cc826 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py @@ -32,41 +32,35 @@ def unassign_constraint( 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() + return usecase.execute(products, constraint) class Usecase: - def execute(self): - products = set(self.settings["products"]) - if not products: - return + file: ifcopenshell.file - self.constraint = self.settings["constraint"] - rels = self.get_constraint_rels() + def execute(self, products_: list[ifcopenshell.entity_instance], constraint: ifcopenshell.entity_instance): + if not products_: + return + products_set = set(products_) + + rels = self.get_constraint_rels(constraint) related_objects = set() for rel in rels: related_objects.update(rel.RelatedObjects) - if not related_objects.intersection(products): + if not related_objects.intersection(products_set): return for rel in rels: related_objects = set(rel.RelatedObjects) - if not related_objects.intersection(products): + if not related_objects.intersection(products_set): continue - related_objects -= products + related_objects -= products_set if related_objects: rel.RelatedObjects = list(related_objects) ifcopenshell.api.owner.update_owner_history(self.file, **{"element": rel}) @@ -77,9 +71,9 @@ class Usecase: if history: ifcopenshell.util.element.remove_deep2(self.file, history) - def get_constraint_rels(self) -> list[ifcopenshell.entity_instance]: + def get_constraint_rels(self, cosntraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: rels = [] - for rel in self.file.get_inverse(self.constraint): + for rel in self.file.get_inverse(cosntraint): if rel.is_a("IfcRelAssociatesConstraint"): rels.append(rel) return rels diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py index ba52e83a9e..1a7d567c58 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py @@ -27,11 +27,8 @@ def edit_context(file: ifcopenshell.file, context: ifcopenshell.entity_instance, 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 :return: None - :rtype: None Example: @@ -47,7 +44,5 @@ def edit_context(file: ifcopenshell.file, context: ifcopenshell.entity_instance, ifcopenshell.api.context.edit_context(model, context=body, attributes={"ContextIdentifier": "Body"}) """ - settings = {"context": context, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["context"], name, value) + for name, value in attributes.items(): + setattr(context, name, value) 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 ac6b563960..fcf298ff7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py @@ -25,8 +25,6 @@ from typing import Union def copy_cost_item( file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance ) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]: - # TODO: currently it never returns list of duplicated cost items - # though it is stated in the docs """Copies all cost items and related relationships The following relationships are also duplicated: @@ -36,9 +34,7 @@ def copy_cost_item( * 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[ifcopenshell.entity_instance] Example: .. code:: python @@ -53,22 +49,29 @@ def copy_cost_item( """ usecase = Usecase() usecase.file = file - usecase.settings = {"cost_item": cost_item} - return usecase.execute() + return usecase.execute(cost_item) class Usecase: - def execute(self): - self.new_cost_items = [] - return self.duplicate_cost_item(self.settings["cost_item"]) + file: ifcopenshell.file + new_cost_items: list[ifcopenshell.entity_instance] - def duplicate_cost_item(self, cost_item): + def execute( + self, cost_item: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]: + self.new_cost_items = [] + self.duplicate_cost_item(cost_item) + return self.new_cost_items[0] if len(self.new_cost_items) == 1 else self.new_cost_items + + def duplicate_cost_item(self, cost_item: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: new_cost_item = ifcopenshell.util.element.copy_deep(self.file, cost_item) self.new_cost_items.append(new_cost_item) self.copy_indirect_attributes(cost_item, new_cost_item) return new_cost_item - def copy_indirect_attributes(self, from_element, to_element): + def copy_indirect_attributes( + self, from_element: ifcopenshell.entity_instance, to_element: ifcopenshell.entity_instance + ) -> None: for inverse in self.file.get_inverse(from_element): if inverse.is_a("IfcRelDefinesByProperties"): inverse = ifcopenshell.util.element.copy(self.file, inverse) 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 2417c9ac65..813e306bc8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py @@ -28,11 +28,8 @@ def edit_cost_item( 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_cost_item( item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule) ifcopenshell.api.cost.edit_cost_item(model, cost_item=item, attributes={"Name": "Foo"}) """ - settings = {"cost_item": cost_item, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["cost_item"], name, value) + for name, value in attributes.items(): + setattr(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 cb265318ac..697d028a37 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 @@ -28,11 +28,8 @@ def edit_cost_item_quantity( 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 :return: None - :rtype: None Example: @@ -50,7 +47,5 @@ def edit_cost_item_quantity( ifcopenshell.api.cost.edit_cost_item_quantity(model, physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) """ - settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["physical_quantity"], name, value) + for name, value in attributes.items(): + setattr(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 1c6f42b123..d55e93330e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py @@ -28,11 +28,8 @@ def edit_cost_schedule( 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 :return: None - :rtype: None Example: @@ -42,8 +39,5 @@ def edit_cost_schedule( ifcopenshell.api.cost.edit_cost_schedule(model, cost_schedule=schedule, attributes={"Name": "Foo"}) """ - - settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["cost_schedule"], name, value) + for name, value in attributes.items(): + setattr(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 f3e4e01acd..5812cbcf75 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py @@ -31,11 +31,8 @@ def edit_cost_value( 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 :return: None - :rtype: None Example: @@ -49,14 +46,12 @@ def edit_cost_value( ifcopenshell.api.cost.edit_cost_value(model, cost_value=value, attributes={"AppliedValue": 42.0}) """ - settings = {"cost_value": cost_value, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): + for name, value in 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 + old_unit_basis = cost_value.UnitBasis if value: value_component = file.create_entity( ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), @@ -65,4 +60,4 @@ def edit_cost_value( 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) + setattr(cost_value, name, value) 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 bb815647fd..de180d65ef 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 @@ -30,12 +30,9 @@ def unassign_cost_item_quantity( 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: @@ -69,38 +66,39 @@ def unassign_cost_item_quantity( """ usecase = Usecase() usecase.file = file - usecase.settings = {"cost_item": cost_item, "products": products or []} - return usecase.execute() + return usecase.execute(cost_item, products or []) class Usecase: - def execute(self): - self.quantities = set(self.settings["cost_item"].CostQuantities or []) - for quantity in self.settings["cost_item"].CostQuantities or []: + file: ifcopenshell.file + + def execute(self, cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]) -> None: + quantities = set(cost_item.CostQuantities or []) + for quantity in cost_item.CostQuantities or []: for inverse in self.file.get_inverse(quantity): if not inverse.is_a("IfcElementQuantity"): continue for rel in inverse.DefinesOccurrence or []: for related_object in rel.RelatedObjects: - if related_object in self.settings["products"]: - self.quantities.remove(quantity) - self.settings["cost_item"].CostQuantities = list(self.quantities) - for product in self.settings["products"]: + if related_object in products: + quantities.remove(quantity) + cost_item.CostQuantities = list(quantities) + for product in products: ifcopenshell.api.control.unassign_control( self.file, related_object=product, - relating_control=self.settings["cost_item"], + relating_control=cost_item, ) - self.update_cost_item_count() + self.update_cost_item_count(cost_item) - def update_cost_item_count(self): + def update_cost_item_count(self, cost_item: ifcopenshell.entity_instance) -> None: # 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 len(self.settings["cost_item"].CostQuantities) == 1: - quantity = self.settings["cost_item"].CostQuantities[0] + if len(cost_item.CostQuantities) == 1: + quantity = cost_item.CostQuantities[0] if quantity.is_a("IfcQuantityCount"): count = 0 - for rel in self.settings["cost_item"].Controls: + for rel in cost_item.Controls: count += len(rel.RelatedObjects) if count: quantity[3] = count diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py index 5627cc1c84..a86a178e86 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py @@ -41,15 +41,12 @@ def assign_document( :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: @@ -65,43 +62,41 @@ def assign_document( # Let's imagine storey represents an IfcBuildingStorey for the ground floor ifcopenshell.api.document.assign_document(model, products=[storey], document=reference) """ - settings = { - "products": products, - "document": document, - } # 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(settings["document"]) - products: set[ifcopenshell.entity_instance] = set(settings["products"]) - products = products - referenced_elements + referenced_elements = ifcopenshell.util.element.get_referenced_elements(document) + products_set: set[ifcopenshell.entity_instance] = set(products) + products_set = products_set - referenced_elements - if not products: + if not products_set: return if file.schema == "IFC2X3": rel = next( - (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == settings["document"]), + (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == document), None, ) else: - ifc_class = settings["document"].is_a() + ifc_class = document.is_a() if ifc_class == "IfcDocumentReference": - rel = next(iter(settings["document"].DocumentRefForObjects), None) + rel = next(iter(document.DocumentRefForObjects), None) elif ifc_class == "IfcDocumentInformation": - rel = next(iter(settings["document"].DocumentInfoForObjects), None) + rel = next(iter(document.DocumentInfoForObjects), None) + else: + assert False, f"Unexpected document type: {ifc_class}" if not rel: return file.create_entity( "IfcRelAssociatesDocument", GlobalId=ifcopenshell.guid.new(), OwnerHistory=ifcopenshell.api.owner.create_owner_history(file), - RelatedObjects=list(products), - RelatingDocument=settings["document"], + RelatedObjects=list(products_set), + RelatingDocument=document, ) - related_objects = set(rel.RelatedObjects) | products + related_objects = set(rel.RelatedObjects) | products_set rel.RelatedObjects = list(related_objects) ifcopenshell.api.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 09d697f7db..56c1d40df4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py @@ -30,11 +30,8 @@ def edit_information( 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 :return: None - :rtype: None Example: @@ -46,7 +43,5 @@ def edit_information( attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", "Location": "A-GA-6100 - Overall Plan.pdf"}) """ - settings = {"information": information, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["information"], name, value) + for name, value in attributes.items(): + setattr(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 5a00827305..51ad195bf3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py @@ -30,11 +30,8 @@ def edit_reference( 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 :return: None - :rtype: None Example: @@ -49,7 +46,5 @@ def edit_reference( ifcopenshell.api.document.edit_reference(model, reference=reference, attributes={"Identification": "2.1.15"}) """ - settings = {"reference": reference, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["reference"], name, value) + for name, value in attributes.items(): + setattr(reference, name, value) 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 1f7356e989..c083960e59 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py @@ -28,11 +28,8 @@ def edit_text_literal( 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_text_literal( ifcopenshell.api.drawing.edit_text_literal(model, text_literal=text, attributes={"Literal": "MY ANNOTATION"}) """ - settings = {"text_literal": text_literal, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["text_literal"], name, value) + for name, value in attributes.items(): + setattr(text_literal, name, value) 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 1883374b97..174c475f92 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py @@ -17,7 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.unit -from typing import Union +from typing import Union, Any COORD = Union[tuple[float, float], tuple[float, float, float]] @@ -82,6 +82,9 @@ def add_axis_representation( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) is_2d = len(self.settings["axis"][0]) == 2 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 6b48845e4e..9d5fe9f20b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py @@ -26,14 +26,9 @@ def add_footprint_representation( # A list of IFC curves to include in the curve set curves: list[ifcopenshell.entity_instance], ) -> ifcopenshell.entity_instance: - settings = { - "context": context, - "curves": curves, - } - return file.createIfcShapeRepresentation( - settings["context"], - settings["context"].ContextIdentifier, + context, + context.ContextIdentifier, "GeometricCurveSet", - [file.createIfcGeometricCurveSet(settings["curves"])], + [file.createIfcGeometricCurveSet(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 efe6aa5c89..940315621a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py @@ -17,7 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.unit -from typing import Optional +from typing import Optional, Any COORD_3D = tuple[float, float, float] @@ -60,6 +60,9 @@ def add_mesh_representation( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 17cdec58c8..77bb515996 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py @@ -52,6 +52,9 @@ def add_profile_representation( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 22fb99d583..fdc457367b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py @@ -426,6 +426,9 @@ def add_window_representation( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): builder = ShapeBuilder(self.file) np_X, np_Y, np_Z = 0, 1, 2 diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py index 571c5212ba..81fa276a2c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py @@ -31,53 +31,33 @@ def connect_path( related_connection: str = "NOTDEFINED", description: Optional[str] = None, ) -> ifcopenshell.entity_instance: - settings = { - "relating_element": relating_element, - "related_element": related_element, - "relating_connection": relating_connection, - "related_connection": related_connection, - "description": description, - } - - incompatible_connections = [] - for rel in settings["relating_element"].ConnectedTo: + incompatible_connections: list[ifcopenshell.entity_instance] = [] + for rel in relating_element.ConnectedTo: if not rel.is_a("IfcRelConnectsPathElements"): continue - if rel.RelatedElement == settings["related_element"]: + if rel.RelatedElement == related_element: incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == settings["relating_connection"] - ): + elif rel.RelatingConnectionType in ["ATSTART", "ATEND"] and rel.RelatingConnectionType == relating_connection: incompatible_connections.append(rel) - for rel in settings["relating_element"].ConnectedFrom: + for rel in relating_element.ConnectedFrom: if not rel.is_a("IfcRelConnectsPathElements"): continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == settings["relating_connection"] - ): + if rel.RelatedConnectionType in ["ATSTART", "ATEND"] and rel.RelatedConnectionType == relating_connection: incompatible_connections.append(rel) - for rel in settings["related_element"].ConnectedFrom: + for rel in related_element.ConnectedFrom: if not rel.is_a("IfcRelConnectsPathElements"): continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == settings["related_connection"] - ): + if rel.RelatedConnectionType in ["ATSTART", "ATEND"] and rel.RelatedConnectionType == related_connection: incompatible_connections.append(rel) - for rel in settings["related_element"].ConnectedTo: + for rel in related_element.ConnectedTo: if not rel.is_a("IfcRelConnectsPathElements"): continue - if rel.RelatedElement == settings["relating_element"]: + if rel.RelatedElement == relating_element: incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == settings["related_connection"] - ): + elif rel.RelatingConnectionType in ["ATSTART", "ATEND"] and rel.RelatingConnectionType == related_connection: incompatible_connections.append(rel) if incompatible_connections: @@ -87,14 +67,15 @@ def connect_path( if history: ifcopenshell.util.element.remove_deep2(file, history) - return file.createIfcRelConnectsPathElements( + return file.create_entity( + "IfcRelConnectsPathElements", ifcopenshell.guid.new(), OwnerHistory=ifcopenshell.api.owner.create_owner_history(file), - Description=settings["description"], - RelatingElement=settings["relating_element"], - RelatedElement=settings["related_element"], - RelatingConnectionType=settings["relating_connection"], - RelatedConnectionType=settings["related_connection"], + Description=description, + RelatingElement=relating_element, + RelatedElement=related_element, + RelatingConnectionType=relating_connection, + RelatedConnectionType=related_connection, RelatingPriorities=[], RelatedPriorities=[], ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py index 64a4f571dc..4972058fb4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py @@ -46,29 +46,25 @@ def assign_group( ifcopenshell.api.group.assign_group(model, products=model.by_type("IfcFurniture"), group=group) """ - settings = { - "products": products, - "group": group, - } - - if not settings["products"]: + if not products: return - if not settings["group"].IsGroupedBy: + is_grouped_by: tuple[ifcopenshell.entity_instance, ...] + if not (is_grouped_by := group.IsGroupedBy): return file.create_entity( "IfcRelAssignsToGroup", **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "RelatedObjects": settings["products"], - "RelatingGroup": settings["group"], - } + "RelatedObjects": products, + "RelatingGroup": group, + }, ) - rel = settings["group"].IsGroupedBy[0] + rel = is_grouped_by[0] related_objects = set(rel.RelatedObjects) or set() - products = set(settings["products"]) - if products.issubset(related_objects): + products_set = set(products) + if products_set.issubset(related_objects): return rel - rel.RelatedObjects = list(related_objects | products) + rel.RelatedObjects = list(related_objects | products_set) ifcopenshell.api.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 a404ef0f88..5a7a2b2018 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py @@ -26,11 +26,8 @@ def edit_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance, att 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 :return: None - :rtype: None Example: @@ -40,7 +37,5 @@ def edit_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance, att ifcopenshell.api.group.edit_group(model, group=group, attributes={"Description": "All furniture and joinery included in the unit"}) """ - settings = {"group": group, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["group"], name, value) + for name, value in attributes.items(): + setattr(group, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py index ee31336eec..cac87106aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py @@ -46,17 +46,12 @@ def unassign_group( bad_furniture = furniture[0] ifcopenshell.api.group.unassign_group(model, products=[bad_furniture], group=group) """ - settings = { - "products": products, - "group": group, - } - - if not settings["group"].IsGroupedBy: + if not group.IsGroupedBy: return - rel = settings["group"].IsGroupedBy[0] + rel = group.IsGroupedBy[0] related_objects = set(rel.RelatedObjects) or set() - products = set(settings["products"]) - related_objects -= products + products_set = set(products) + related_objects -= products_set if related_objects: rel.RelatedObjects = list(related_objects) ifcopenshell.api.owner.update_owner_history(file, **{"element": rel}) 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 61b3ec7657..468c193ff7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py @@ -45,24 +45,19 @@ def update_group_products( ifcopenshell.api.group.update_group_products(model, products=model.by_type("IfcFurniture"), group=group) """ - settings = { - "group": group, - "products": products, - } - - if not settings["group"].IsGroupedBy: + if not group.IsGroupedBy: return file.create_entity( "IfcRelAssignsToGroup", **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "RelatedObjects": settings["products"], - "RelatingGroup": settings["group"], + "RelatedObjects": products, + "RelatingGroup": group, } ) else: - rels = settings["group"].IsGroupedBy - objects = set(settings["products"]) + rels = group.IsGroupedBy + objects = set(products) for rel in rels: objects.update([g for g in rel.RelatedObjects if g.is_a("IfcGroup")]) to_purge = rels[1:] diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py index 6f55f5da1d..7203fabb0c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py @@ -37,7 +37,5 @@ def edit_layer(file: ifcopenshell.file, layer: ifcopenshell.entity_instance, att ifcopenshell.api.layer.edit_layer(model, layer=layer, attributes={"Description": "All walls, based on the AIA standard."}) """ - settings = {"layer": layer, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["layer"], name, value) + for name, value in attributes.items(): + setattr(layer, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py index 97ae094a50..3eb0ed8fa1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py @@ -61,17 +61,11 @@ def unassign_layer( # Let's undo it! ifcopenshell.api.layer.unassign_layer(model, items=[representation.Items[0]], layer=layer) """ - settings = { - "items": items, - "layer": layer, - } - - layer = settings["layer"] assigned_items = set(layer.AssignedItems) or set() - items = set(settings["items"]) - if not items.issubset(assigned_items): + items_set = set(items) + if not items_set.issubset(assigned_items): return - assigned_items = list(assigned_items - items) + assigned_items = list(assigned_items - items_set) # keep IFC valid in case if there are no items left if assigned_items: diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py index b3bc87b1c3..4e12195bda 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py @@ -28,11 +28,8 @@ def edit_library(file: ifcopenshell.file, library: ifcopenshell.entity_instance, 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 :return: None - :rtype: None Example: diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py index f3016fd9cf..e6acacbd8f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py @@ -28,11 +28,8 @@ def edit_reference( 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 :return: None - :rtype: None Example: @@ -44,7 +41,5 @@ def edit_reference( ifcopenshell.api.library.edit_reference(model, reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) """ - settings = {"reference": reference, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["reference"], name, value) + for name, value in attributes.items(): + setattr(reference, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 7719fd86c8..24fa93487b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.representation +from typing import Any def assign_profile( @@ -97,6 +98,7 @@ def assign_profile( class Usecase: file: ifcopenshell.file + settings: dict[str, Any] def execute(self) -> None: # TODO: handle composite profiles 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 939e995a06..9a01322743 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py @@ -28,11 +28,8 @@ def edit_assigned_material( 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_assigned_material( ifcopenshell.api.material.edit_assigned_material(model, element=concrete, attributes={"Description": "40MPA concrete with broom finish"}) """ - settings = {"element": element, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["element"], name, value) + for name, value in attributes.items(): + setattr(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 eda33d349b..a7398ca5f0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py @@ -31,13 +31,9 @@ def edit_constituent( 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 Example: @@ -65,8 +61,6 @@ def edit_constituent( ifcopenshell.api.material.edit_constituent(model, constituent=constituent, attributes={"Name": "Glazing"}) """ - settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} - - for name, value in settings["attributes"].items(): - setattr(settings["constituent"], name, value) - settings["constituent"].Material = settings["material"] + for name, value in (attributes or {}).items(): + setattr(constituent, name, value) + constituent.Material = material diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py index d691d6b154..68f3c6d832 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py @@ -31,14 +31,10 @@ def edit_layer( 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 Example: @@ -63,9 +59,7 @@ def edit_layer( layer = ifcopenshell.api.material.add_layer(model, layer_set=material_set, material=gypsum) ifcopenshell.api.material.edit_layer(model, layer=layer, attributes={"LayerThickness": 13}) """ - settings = {"layer": layer, "attributes": attributes or {}, "material": material} - - for name, value in settings["attributes"].items(): - setattr(settings["layer"], name, value) - if settings["material"]: - settings["layer"].Material = settings["material"] + for name, value in (attributes or {}).items(): + setattr(layer, name, value) + if material: + layer.Material = 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 6c581c1374..9d1ffb14ec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py @@ -29,11 +29,8 @@ def edit_layer_usage(file: ifcopenshell.file, usage: ifcopenshell.entity_instanc 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 :return: None - :rtype: None Example: @@ -75,7 +72,5 @@ def edit_layer_usage(file: ifcopenshell.file, usage: ifcopenshell.entity_instanc ifcopenshell.api.material.edit_layer_usage(model, usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200}) """ - settings = {"usage": usage, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["usage"], name, value) + for name, value in attributes.items(): + setattr(usage, 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 17f925997e..c35755a960 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -34,17 +34,12 @@ def edit_profile( 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 Example: @@ -80,16 +75,9 @@ def edit_profile( ifcopenshell.api.material.edit_profile(model, profile=profile_item, profile_def=hea200, material=steel2) """ - settings = { - "profile": profile, - "attributes": attributes or {}, - "profile_def": profile_def, - "material": material, - } - - 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"] + for name, value in (attributes or {}).items(): + setattr(profile, name, value) + if material: + profile.Material = material + if profile_def: + profile.Profile = 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 f9894db5b3..4a659f251c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py @@ -17,6 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.geom import ifcopenshell.util.representation +from ifcopenshell.geom import ShapeType from typing import Any @@ -34,11 +35,8 @@ def edit_profile_usage( 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 :return: None - :rtype: None Example: @@ -98,16 +96,19 @@ def edit_profile_usage( class Usecase: - def execute(self): - self.cardinal_point = self.settings["attributes"].get("CardinalPoint") - if self.cardinal_point and self.cardinal_point != self.settings["usage"].CardinalPoint: + file: ifcopenshell.file + + def execute(self, usage: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None: + self.attributes = attributes + self.cardinal_point = attributes.get("CardinalPoint") + if self.cardinal_point and self.cardinal_point != usage.CardinalPoint: self.update_cardinal_point() - for name, value in self.settings["attributes"].items(): - setattr(self.settings["usage"], name, value) + for name, value in attributes.items(): + setattr(usage, name, value) def update_cardinal_point(self): - material_set = self.settings["usage"].ForProfileSet + material_set = self.attributes["usage"].ForProfileSet self.profile = material_set.CompositeProfile if not self.profile and material_set.MaterialProfiles: self.profile = material_set.MaterialProfiles[0].Profile @@ -117,13 +118,13 @@ class Usecase: self.position = self.calculate_position() if self.file.schema == "IFC2X3": - for rel in self.file.get_inverse(self.settings["usage"]): + for rel in self.file.get_inverse(self.attributes["usage"]): if not rel.is_a("IfcRelAssociatesMaterial"): continue for element in rel.RelatedObjects: self.update_representation(element) else: - for rel in self.settings["usage"].AssociatedTo: + for rel in self.attributes["usage"].AssociatedTo: for element in rel.RelatedObjects: self.update_representation(element) @@ -166,7 +167,7 @@ class Usecase: elif self.cardinal_point == 9: return self.get_top_right(shape) - def get_bottom_left(self, shape): + def get_bottom_left(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] y = [v[i + 1] for i in range(0, len(v), 3)] @@ -174,13 +175,13 @@ class Usecase: height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, height / 2, 0.0))) - def get_bottom_centre(self, shape): + def get_bottom_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts y = [v[i + 1] for i in range(0, len(v), 3)] height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, height / 2, 0.0))) - def get_bottom_right(self, shape): + def get_bottom_right(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] y = [v[i + 1] for i in range(0, len(v), 3)] @@ -188,22 +189,22 @@ class Usecase: height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, height / 2, 0.0))) - def get_mid_depth_left(self, shape): + def get_mid_depth_left(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] width = max(x) - min(x) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, 0.0, 0.0))) - def get_mid_depth_centre(self, shape): + def get_mid_depth_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance: return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))) - def get_mid_depth_right(self, shape): + def get_mid_depth_right(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] width = max(x) - min(x) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, 0.0, 0.0))) - def get_top_left(self, shape): + def get_top_left(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] y = [v[i + 1] for i in range(0, len(v), 3)] @@ -211,13 +212,13 @@ class Usecase: height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((-width / 2, -height / 2, 0.0))) - def get_top_centre(self, shape): + def get_top_centre(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts y = [v[i + 1] for i in range(0, len(v), 3)] height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((0.0, -height / 2, 0.0))) - def get_top_right(self, shape): + def get_top_right(self, shape: ShapeType) -> ifcopenshell.entity_instance: v = shape.verts x = [v[i] for i in range(0, len(v), 3)] y = [v[i + 1] for i in range(0, len(v), 3)] @@ -225,7 +226,7 @@ class Usecase: height = max(y) - min(y) return self.file.createIfcAxis2Placement3D(self.file.createIfcCartesianPoint((width / 2, -height / 2, 0.0))) - def update_representation(self, element): + def update_representation(self, element: ifcopenshell.entity_instance) -> None: representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if not representation: return @@ -234,5 +235,5 @@ class Usecase: if subelement.is_a("IfcSweptAreaSolid") and subelement.SweptArea == self.profile: self.update_swept_area_solid(subelement) - def update_swept_area_solid(self, element): + def update_swept_area_solid(self, element: ifcopenshell.entity_instance) -> None: element.Position = self.position diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py index 7521c48a15..d9401cfa6d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell +import ifcopenshell.util.element def remove_constituent( 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 cd4f2f1cd6..6af15a7d3a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -54,8 +54,6 @@ def remove_list_item( # Let's remove the glass ifcopenshell.api.material.remove_list_item(model, material_list=material_set, material_index=1) """ - settings = {"material_list": material_list, "material_index": material_index} - - materials = list(settings["material_list"].Materials) - materials.pop(settings["material_index"]) - settings["material_list"].Materials = materials + materials = list(material_list.Materials) + materials.pop(material_index) + material_list.Materials = materials 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 0015d3558a..aa06433e66 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py @@ -57,16 +57,17 @@ def reorder_set_item( ifcopenshell.api.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} - - if settings["material_set"].is_a("IfcMaterialConstituentSet"): + if material_set.is_a("IfcMaterialConstituentSet"): set_name = "MaterialConstituents" - elif settings["material_set"].is_a("IfcMaterialLayerSet"): + elif material_set.is_a("IfcMaterialLayerSet"): set_name = "MaterialLayers" - elif settings["material_set"].is_a("IfcMaterialProfileSet"): + elif material_set.is_a("IfcMaterialProfileSet"): set_name = "MaterialProfiles" - elif settings["material_set"].is_a("IfcMaterialList"): + elif 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) + else: + raise ValueError(f"Unexpected material set type: '{material_set.is_a()}'.") + + items = list(getattr(material_set, set_name) or []) + items.insert(new_index, items.pop(old_index)) + setattr(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 8db2060df0..f8386490c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py @@ -19,6 +19,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.util.element +from typing import Any def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: @@ -58,6 +59,9 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): self.products = set(self.settings["products"]) if not self.products: diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py index 991006be01..3f18754287 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py @@ -17,7 +17,7 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.api -from typing import Optional +from typing import Optional, Any def add_application( @@ -68,6 +68,9 @@ def add_application( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py index 7105e5e78d..75bb5eb517 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py @@ -48,16 +48,14 @@ def add_role( identification="AWB", name="Architects Without Ballpens") ifcopenshell.api.owner.add_role(model, assigned_object=organisation, role="ARCHITECT") """ - settings = {"assigned_object": assigned_object, "role": role} - - element = file.createIfcActorRole("ARCHITECT") - if settings["role"]: + element = file.create_entity("IfcActorRole", Role="ARCHITECT") + if role: try: - element.Role = settings["role"] + element.Role = role except: element.Role = "USERDEFINED" - element.UserDefinedRole = settings["role"] - roles = list(settings["assigned_object"].Roles) if settings["assigned_object"].Roles else [] + element.UserDefinedRole = role + roles = list(assigned_object.Roles) if assigned_object.Roles else [] roles.append(element) - settings["assigned_object"].Roles = roles + assigned_object.Roles = roles return element diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py index 799b37462c..8ad238f994 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py @@ -26,11 +26,8 @@ def edit_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance, att 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 :return: None - :rtype: None Example: @@ -49,7 +46,5 @@ def edit_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance, att ifcopenshell.api.actor.edit_actor(model, actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."}) """ - settings = {"actor": actor, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["actor"], name, value) + for name, value in attributes.items(): + setattr(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 43776fa711..52e80d6ef1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py @@ -26,11 +26,8 @@ def edit_address(file: ifcopenshell.file, address: ifcopenshell.entity_instance, 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 :return: None - :rtype: None Example: @@ -51,7 +48,5 @@ def edit_address(file: ifcopenshell.file, address: ifcopenshell.entity_instance, "ElectronicMailAddresses": ["bobthebuilder@example.com"], "WWWHomePageURL": "https://thinkmoult.com"}) """ - settings = {"address": address, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["address"], name, value) + for name, value in attributes.items(): + setattr(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 9cfbfff48d..a2bb5e3eb6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py @@ -28,11 +28,8 @@ def edit_organisation( 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 :return: None - :rtype: None Example: @@ -43,7 +40,5 @@ def edit_organisation( ifcopenshell.api.owner.edit_organisation(model, organisation=organisation, attributes={"name": "Architects Without Ballpens"}) """ - settings = {"organisation": organisation, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["organisation"], name, value) + for name, value in attributes.items(): + setattr(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 0e4bd15293..6b697bd674 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py @@ -26,11 +26,8 @@ def edit_person(file: ifcopenshell.file, person: ifcopenshell.entity_instance, a 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 :return: None - :rtype: None Example: @@ -41,7 +38,5 @@ def edit_person(file: ifcopenshell.file, person: ifcopenshell.entity_instance, a ifcopenshell.api.owner.edit_person(model, person=person, attributes={"MiddleNames": ["The"], "FamilyName": "Builder"}) """ - settings = {"person": person, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["person"], name, value) + for name, value in attributes.items(): + setattr(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 885d4b9b9b..fca80a16b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py @@ -26,11 +26,8 @@ def edit_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance, attri 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 :return: None - :rtype: None Example: @@ -45,7 +42,5 @@ def edit_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance, attri # But Bob is not an architect ifcopenshell.api.owner.edit_role(model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"}) """ - settings = {"role": role, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["role"], name, value) + for name, value in attributes.items(): + setattr(role, name, value) 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 08892a555b..80f3b8cf71 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py @@ -16,12 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . +import numpy.typing as npt import ifcopenshell.util.unit -from typing import Optional +from ifcopenshell.util.shape_builder import V, SequenceOfVectors, ifc_safe_vector_type +from typing import Optional, Union def add_arbitrary_profile( - file: ifcopenshell.file, profile: list[tuple[float, float]], name: Optional[str] = None + file: ifcopenshell.file, profile: SequenceOfVectors, name: Optional[str] = None ) -> ifcopenshell.entity_instance: """Adds a new arbitrary polyline-based profile @@ -33,13 +35,10 @@ def add_arbitrary_profile( identical. :param profile: A list of coordinates - :type profile: list[tuple[float, 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: @@ -53,26 +52,30 @@ def add_arbitrary_profile( """ usecase = Usecase() usecase.file = file - usecase.settings = {"profile": profile, "name": name} - return usecase.execute() + return usecase.execute(V(profile), name) class Usecase: - 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"]] - if self.file.schema == "IFC2X3": - curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) - else: - dimensions = len(points[0]) - if dimensions == 2: - ifc_points = self.file.createIfcCartesianPointList2D(points) - elif dimensions == 3: - ifc_points = self.file.createIfcCartesianPointList3D(points) - curve = self.file.createIfcIndexedPolyCurve(ifc_points) - return self.file.createIfcArbitraryClosedProfileDef("AREA", self.settings["name"], curve) + file: ifcopenshell.file - def convert_si_to_unit(self, co): - if isinstance(co, (tuple, list)): - return [self.convert_si_to_unit(o) for o in co] - return co / self.settings["unit_scale"] + def execute(self, profile: npt.NDArray, name: Union[str, None]): + self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) + points = self.convert_si_to_unit(profile) + if self.file.schema == "IFC2X3": + curve = self.file.create_entity( + "IfcPolyline", + [self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in points], + ) + else: + dimensions = points.shape[1] + if dimensions == 2: + ifc_points = self.file.create_entity("IfcCartesianPointList2D", ifc_safe_vector_type(points)) + elif dimensions == 3: + ifc_points = self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(points)) + else: + assert False, f"Invalid dimensions: {dimensions}." + curve = self.file.create_entity("IfcIndexedPolyCurve", ifc_points) + return self.file.create_entity("IfcArbitraryClosedProfileDef", "AREA", name, curve) + + def convert_si_to_unit(self, co: npt.NDArray) -> npt.NDArray: + return co / self.unit_scale 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 8412d40e23..b813a3c65f 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 @@ -17,13 +17,15 @@ # along with IfcOpenShell. If not, see . import ifcopenshell.util.unit -from typing import Optional +import numpy.typing as npt +from ifcopenshell.util.shape_builder import SequenceOfVectors, ifc_safe_vector_type, V +from typing import Optional, Union def add_arbitrary_profile_with_voids( file: ifcopenshell.file, - outer_profile: list[tuple[float, float]], - inner_profiles: list[list[tuple[float, float]]], + outer_profile: SequenceOfVectors, + inner_profiles: list[SequenceOfVectors], name: Optional[str] = None, ) -> ifcopenshell.entity_instance: """Adds a new arbitrary polyline-based profile with voids @@ -41,15 +43,11 @@ def add_arbitrary_profile_with_voids( provided in SI meters. :param outer_profile: A list of coordinates - :type profile: list[tuple[float, float]] :param inner_profiles: A list of polylines - :type profile: list[list[tuple[float, 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: @@ -63,37 +61,52 @@ def add_arbitrary_profile_with_voids( """ usecase = Usecase() usecase.file = file - usecase.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name} - return usecase.execute() + return usecase.execute(V(outer_profile), [V(p) for p in inner_profiles], name) class Usecase: - 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"]] - inner_points = [] - for inner_profile in self.settings["inner_profiles"]: - inner_points.append([self.convert_si_to_unit(p) for p in inner_profile]) + file: ifcopenshell.file + + def execute( + self, + outer_profile: npt.NDArray, + inner_profiles: list[npt.NDArray], + name: Union[str, None], + ): + self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) + outer_points = self.convert_si_to_unit(outer_profile) + inner_points: list[npt.NDArray] = [] + for inner_profile in inner_profiles: + inner_points.append(self.convert_si_to_unit(inner_profile)) + + inner_curves: list[ifcopenshell.entity_instance] = [] if self.file.schema == "IFC2X3": - outer_curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in outer_points]) - inner_curves = [] + outer_curve = self.file.create_entity( + "IfcPolyline", + [self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in outer_points], + ) for inner_point in inner_points: inner_curves.append( - self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point]) + self.file.create_entity( + "IfcPolyline", + [self.file.create_entity("IfcCartesianPoint", ifc_safe_vector_type(p)) for p in inner_point], + ) ) else: - outer_curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(outer_points)) - inner_curves = [] + outer_curve = self.file.create_entity( + "IfcIndexedPolyCurve", + (self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(outer_points))), + ) for inner_point in inner_points: - dimensions = len(inner_point[0]) + dimensions = inner_point.shape[1] if dimensions == 2: - ifc_points = self.file.createIfcCartesianPointList2D(inner_point) + ifc_points = self.file.create_entity("IfcCartesianPointList2D", ifc_safe_vector_type(inner_point)) elif dimensions == 3: - ifc_points = self.file.createIfcCartesianPointList3D(inner_point) - inner_curves.append(self.file.createIfcIndexedPolyCurve(ifc_points)) - return self.file.createIfcArbitraryProfileDefWithVoids("AREA", self.settings["name"], outer_curve, inner_curves) + ifc_points = self.file.create_entity("IfcCartesianPointList3D", ifc_safe_vector_type(inner_point)) + else: + assert False, f"Invalid dimensions: {dimensions}." + inner_curves.append(self.file.create_entity("IfcIndexedPolyCurve", ifc_points)) + return self.file.create_entity("IfcArbitraryProfileDefWithVoids", "AREA", name, outer_curve, inner_curves) - def convert_si_to_unit(self, co): - if isinstance(co, (tuple, list)): - return [self.convert_si_to_unit(o) for o in co] - return co / self.settings["unit_scale"] + def convert_si_to_unit(self, co: npt.NDArray) -> npt.NDArray: + return co / self.unit_scale diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py index b1fda065f0..ba7bc3cfeb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py @@ -26,11 +26,8 @@ def edit_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance, 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 :return: None - :rtype: None Example: @@ -43,7 +40,5 @@ def edit_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instance, ifcopenshell.api.profile.edit_profile(model, profile=circle, attributes={"ProfileName": "1000mm Dia"}) """ - settings = {"profile": profile, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["profile"], name, value) + for name, value in attributes.items(): + setattr(profile, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py index ffa31faa8e..db585c3276 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py @@ -20,7 +20,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.api.pset import ifcopenshell.guid -from typing import Optional +from typing import Optional, Any def add_pset( @@ -92,12 +92,11 @@ def add_pset( # Add a fire rating property standardised by buildingSMART. ifcopenshell.api.pset.edit_pset(model, pset=pset, properties={"FireRating": "2HR"}) """ - settings = {"product": product, "name": name} is_ifc2x3 = file.schema == "IFC2X3" - 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"]: + if product.is_a("IfcObject") or product.is_a("IfcContext"): + for rel in product.IsDefinedBy or []: + if rel.is_a("IfcRelDefinesByProperties") and rel.RelatingPropertyDefinition.Name == name: return rel.RelatingPropertyDefinition pset = file.create_entity( @@ -105,15 +104,15 @@ def add_pset( **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "Name": settings["name"], + "Name": name, }, ) - ifcopenshell.api.pset.assign_pset(file, [settings["product"]], pset) + ifcopenshell.api.pset.assign_pset(file, [product], pset) return pset - elif settings["product"].is_a("IfcTypeObject"): - for definition in settings["product"].HasPropertySets or []: - if definition.Name == settings["name"]: + elif product.is_a("IfcTypeObject"): + for definition in product.HasPropertySets or []: + if definition.Name == name: return definition pset = file.create_entity( @@ -121,42 +120,43 @@ def add_pset( **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "Name": settings["name"], + "Name": name, }, ) - ifcopenshell.api.pset.assign_pset(file, [settings["product"]], pset) + ifcopenshell.api.pset.assign_pset(file, [product], pset) return pset # in IFC2X3 IfcMaterialDefinition not yet existed - elif settings["product"].is_a("IfcMaterialDefinition") or settings["product"].is_a("IfcMaterial"): - kwargs = {"Material": settings["product"]} + elif product.is_a("IfcMaterialDefinition") or product.is_a("IfcMaterial"): + kwargs: dict[str, Any] + kwargs = {"Material": product} if file.schema == "IFC2X3": ifc_class = ifc2x3_subclass or "IfcExtendedMaterialProperties" - definitions = (d for d in file.by_type("IfcMaterialProperties") if d.Material == settings["product"]) + definitions = (d for d in file.by_type("IfcMaterialProperties") if d.Material == product) if ifc_class == "IfcExtendedMaterialProperties": - kwargs["Name"] = settings["name"] + kwargs["Name"] = name else: ifc_class = "IfcMaterialProperties" - definitions = settings["product"].HasProperties - kwargs["Name"] = settings["name"] + definitions = product.HasProperties + kwargs["Name"] = name for definition in definitions: # In IFC2X3 not all IfcMaterialProperties has Name - if getattr(definition, "Name", None) == settings["name"]: + if getattr(definition, "Name", None) == name: return definition return file.create_entity(ifc_class, **kwargs) - elif settings["product"].is_a("IfcProfileDef"): + elif product.is_a("IfcProfileDef"): # in IFC2X3 IfcProfileProperties doesn't have Name and we cannot identify them if file.schema != "IFC2X3": - for definition in settings["product"].HasProperties or []: - if definition.Name == settings["name"]: + for definition in product.HasProperties or []: + if definition.Name == name: return definition kwargs = {} - kwargs["ProfileDefinition"] = settings["product"] + kwargs["ProfileDefinition"] = product if file.schema != "IFC2X3": - kwargs["Name"] = settings["name"] + kwargs["Name"] = name if is_ifc2x3: ifc_class = ifc2x3_subclass or "IfcGeneralProfileProperties" @@ -164,4 +164,4 @@ def add_pset( ifc_class = "IfcProfileProperties" return file.create_entity(ifc_class, **kwargs) - raise TypeError(f"Class '{settings['product'].is_a(True)}' doesn't support adding a property set.") + raise TypeError(f"Class '{product.is_a(True)}' doesn't support adding a property set.") diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py index 15646fe9c7..415b8ec187 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py @@ -19,6 +19,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.guid +from typing import Any def add_qto(file: ifcopenshell.file, product: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance: @@ -83,9 +84,15 @@ def add_qto(file: ifcopenshell.file, product: ifcopenshell.entity_instance, name class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 []: + product: ifcopenshell.entity_instance = self.settings["product"] + name: str = self.settings["name"] + + if product.is_a("IfcObject") or product.is_a("IfcContext"): + for rel in product.IsDefinedBy or []: if ( rel.is_a("IfcRelDefinesByProperties") and rel.RelatingPropertyDefinition.Name == self.settings["name"] @@ -103,14 +110,14 @@ class Usecase: } ) return qto - elif self.settings["product"].is_a("IfcTypeObject"): - for definition in self.settings["product"].HasPropertySets or []: - if definition.Name == self.settings["name"]: + elif product.is_a("IfcTypeObject"): + for definition in product.HasPropertySets or []: + if definition.Name == name: return definition qto = self.create_qto() - has_property_sets = list(self.settings["product"].HasPropertySets or []) + has_property_sets = list(product.HasPropertySets or []) has_property_sets.append(qto) - self.settings["product"].HasPropertySets = has_property_sets + product.HasPropertySets = has_property_sets return qto def create_qto(self): 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 1984506c30..3677a8e63e 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 @@ -28,11 +28,8 @@ def edit_prop_template( 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 :return: None - :rtype: None Example: 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 1682fa150d..d143b5554e 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 @@ -28,11 +28,8 @@ def edit_pset_template( 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 :return: None - :rtype: None Example: @@ -45,7 +42,5 @@ def edit_pset_template( ifcopenshell.api.pset_template.edit_pset_template(model, pset_template=template, attributes={"Name": "ABC_RiskFactors"}) """ - settings = {"pset_template": pset_template, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["pset_template"], name, value) + for name, value in attributes.items(): + setattr(pset_template, name, value) 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 b445c1d1ad..fc67a8d3c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -36,15 +36,12 @@ def add_resource_quantity( 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 Example: @@ -65,8 +62,6 @@ def add_resource_quantity( ifcopenshell.api.resource.edit_resource_quantity(model, physical_quantity=quantity, attributes={"TimeValue": 8.0}) """ - settings = {"resource": resource, "ifc_class": ifc_class} - resource_type = resource.is_a() supported_quantities = ifcopenshell.util.resource.RESOURCES_TO_QUANTITIES[resource_type] if ifc_class not in supported_quantities: @@ -75,14 +70,14 @@ def add_resource_quantity( f"Supported quantities: {','.join(supported_quantities)}" ) - quantity = file.create_entity(settings["ifc_class"], Name="Unnamed") + quantity = file.create_entity(ifc_class, Name="Unnamed") # 3 IfcPhysicalSimpleQuantity Value - if settings["ifc_class"] == "IfcQuantityCount": + if ifc_class == "IfcQuantityCount": quantity[3] = 0 else: quantity[3] = 0.0 - old_quantity = settings["resource"].BaseQuantity - settings["resource"].BaseQuantity = quantity + old_quantity = resource.BaseQuantity + 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/edit_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py index b5bb899ead..3476032c1a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py @@ -26,11 +26,8 @@ def edit_resource(file: ifcopenshell.file, resource: ifcopenshell.entity_instanc 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 :return: None - :rtype: None Example: @@ -42,7 +39,5 @@ def edit_resource(file: ifcopenshell.file, resource: ifcopenshell.entity_instanc # Change the name of the resource to "Zone A Crew" ifcopenshell.api.resource.edit_resource(model, resource=resource, attributes={"Name": "Foo"}) """ - settings = {"resource": resource, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["resource"], name, value) + for name, value in attributes.items(): + setattr(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 af8f55f94f..87a14774d8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -28,11 +28,8 @@ def edit_resource_quantity( 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 :return: None - :rtype: None Example: @@ -53,10 +50,5 @@ def edit_resource_quantity( ifcopenshell.api.resource.edit_resource_quantity(model, physical_quantity=time, attributes={"TimeValue": 8.0}) """ - settings = { - "physical_quantity": physical_quantity, - "attributes": attributes, - } - - for name, value in settings["attributes"].items(): - setattr(settings["physical_quantity"], name, value) + for name, value in attributes.items(): + setattr(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 687fe0490d..7326a5fd90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py @@ -18,6 +18,9 @@ import ifcopenshell import ifcopenshell.api.sequence +import ifcopenshell.util.constraint +import ifcopenshell.util.date +import ifcopenshell.util.resource from typing import Any @@ -30,11 +33,8 @@ def edit_resource_time( 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 :return: None - :rtype: None Example: @@ -62,25 +62,23 @@ def edit_resource_time( """ usecase = Usecase() usecase.file = file - usecase.settings = {"resource_time": resource_time, "attributes": attributes} - return usecase.execute() + return usecase.execute(resource_time, attributes) class Usecase: - def execute(self): - self.resource = self.get_resource() + file: ifcopenshell.file + + def execute(self, resource_time: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None: + resource = self.get_resource(resource_time) # If the user specifies both an end date and a duration, the duration takes priority - if ( - self.settings["attributes"].get("ScheduleWork", None) - 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(): - del self.settings["attributes"]["ActualFinish"] + if attributes.get("ScheduleWork", None) and "ScheduleFinish" in attributes.keys(): + del attributes["ScheduleFinish"] + if attributes.get("ActualWork", None) and "ActualFinish" in attributes.keys(): + del attributes["ActualFinish"] - for name, value in self.settings["attributes"].items(): - metrics = ifcopenshell.util.constraint.get_metric_constraints(self.resource, "Usage." + name) + for name, value in attributes.items(): + metrics = ifcopenshell.util.constraint.get_metric_constraints(resource, "Usage." + name) if metrics and ifcopenshell.util.constraint.is_hard_constraint(metrics[0]): continue if value: @@ -88,13 +86,13 @@ class Usecase: value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["resource_time"], name, value) + setattr(resource_time, name, value) if name == "ScheduleUsage" and ifcopenshell.util.constraint.get_metric_constraints( - self.resource, "Usage.ScheduleWork" + resource, "Usage.ScheduleWork" ): - task = ifcopenshell.util.resource.get_task_assignments(self.resource) + task = ifcopenshell.util.resource.get_task_assignments(resource) if task: ifcopenshell.api.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] + def get_resource(self, resource_time: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: + return next(e for e in self.file.get_inverse(resource_time) if e.is_a("IfcResource")) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index a55c711e53..6f02a4531d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -23,6 +23,7 @@ import ifcopenshell.api.geometry import ifcopenshell.util.system import ifcopenshell.util.element import ifcopenshell.util.placement +from typing import Any def copy_class(file: ifcopenshell.file, product: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: @@ -74,6 +75,9 @@ def copy_class(file: ifcopenshell.file, product: ifcopenshell.entity_instance) - class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 a31dc4c00a..67d67ed7f2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py @@ -19,7 +19,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.guid -from typing import Optional +from typing import Optional, Any def create_entity( @@ -80,6 +80,9 @@ def create_entity( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 3e4cd3faf4..7682856864 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -27,7 +27,7 @@ import ifcopenshell.util.representation import ifcopenshell.util.type import ifcopenshell.util.schema import ifcopenshell.util.element -from typing import Optional, Union, Literal +from typing import Optional, Union, Literal, Any def reassign_class( @@ -87,6 +87,7 @@ def reassign_class( class Usecase: file: ifcopenshell.file + settings: dict[str, Any] def execute(self): ifc_class: str = self.settings["ifc_class"] 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 89037d4eb0..38d64aa1bc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py @@ -29,11 +29,8 @@ def add_task_time( (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 Example: @@ -61,11 +58,9 @@ def add_task_time( ifcopenshell.api.sequence.edit_task_time(model, task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) """ - settings = {"task": task, "is_recurring": is_recurring} - - if settings["is_recurring"]: + if is_recurring: task_time = file.create_entity("IfcTaskTimeRecurring") else: task_time = file.create_entity("IfcTaskTime") - settings["task"].TaskTime = task_time + task.TaskTime = task_time return task_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 65ea1db9bd..a5d813756e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py @@ -34,17 +34,13 @@ def assign_lag_time( 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 Example: @@ -84,16 +80,10 @@ def assign_lag_time( # for whatever reason. ifcopenshell.api.sequence.assign_lag_time(model, rel_sequence=sequence, lag_value="P1D") """ - settings = { - "rel_sequence": rel_sequence, - "lag_value": lag_value, - "duration_type": duration_type, - } - - 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 + lag_value = file.create_entity("IfcDuration", ifcopenshell.util.date.datetime2ifc(lag_value, "IfcDuration")) + lag_time = file.create_entity("IfcLagTime", DurationType=duration_type, LagValue=lag_value) + if rel_sequence.is_a("IfcRelSequence"): + if rel_sequence.TimeLag and len(file.get_inverse(rel_sequence.TimeLag)) == 1: + file.remove(rel_sequence.TimeLag) + rel_sequence.TimeLag = lag_time return lag_time 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 0c64fb09e2..333aefb418 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py @@ -65,11 +65,8 @@ def assign_recurrence_pattern( :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: @@ -108,16 +105,14 @@ def assign_recurrence_pattern( ifcopenshell.api.sequence.edit_recurrence_pattern(model, recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6}) """ - settings = {"parent": parent, "recurrence_type": recurrence_type} + recurrence = file.create_entity("IfcRecurrencePattern", recurrence_type) - recurrence = file.createIfcRecurrencePattern(settings["recurrence_type"]) - - 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 recurrence_old := settings["parent"].Recurrence and len(file.get_inverse(recurrence_old)) == 1: + if parent.is_a("IfcWorkTime"): + if parent.RecurrencePattern and len(file.get_inverse(parent.RecurrencePattern)) == 1: + file.remove(parent.RecurrencePattern) + parent.RecurrencePattern = recurrence + elif parent.is_a("IfcTaskTimeRecurring"): + if (recurrence_old := parent.Recurrence) and len(file.get_inverse(recurrence_old)) == 1: file.remove(recurrence_old) - settings["parent"].Recurrence = recurrence + 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 664d5780ec..65491604aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py @@ -51,13 +51,10 @@ def assign_sequence( 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 Example: @@ -109,24 +106,18 @@ def assign_sequence( # to be 2000-01-05. ifcopenshell.api.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"]: + for rel in related_process.IsSuccessorFrom or []: + if rel.RelatingProcess == relating_process: return rel rel = file.create_entity( "IfcRelSequence", **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "RelatingProcess": settings["relating_process"], - "RelatedProcess": settings["related_process"], - "SequenceType": settings["sequence_type"], + "RelatingProcess": relating_process, + "RelatedProcess": related_process, + "SequenceType": sequence_type, } ) - ifcopenshell.api.sequence.cascade_schedule(file, task=settings["relating_process"]) + ifcopenshell.api.sequence.cascade_schedule(file, task=relating_process) return rel 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 7c3f821e7e..b8b4d857b6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py @@ -20,6 +20,7 @@ import math import ifcopenshell.api.sequence import ifcopenshell.util.date import ifcopenshell.util.element +from typing import Union def calculate_task_duration(file: ifcopenshell.file, task: ifcopenshell.entity_instance) -> None: @@ -35,9 +36,7 @@ def calculate_task_duration(file: ifcopenshell.file, task: ifcopenshell.entity_i then nothing happens. :param task: The IfcTask to calculate the duration for. - :type task: ifcopenshell.entity_instance :return: None - :rtype: None Example: @@ -82,18 +81,20 @@ def calculate_task_duration(file: ifcopenshell.file, task: ifcopenshell.entity_i """ usecase = Usecase() usecase.file = file - usecase.settings = {"task": task} - return usecase.execute() + return usecase.execute(task) class Usecase: - def execute(self): + file: ifcopenshell.file + + def execute(self, task: ifcopenshell.entity_instance) -> None: + self.task = task self.seconds_per_workday = self.calculate_seconds_per_workday() duration = self.calculate_max_resource_usage_duration() if duration: self.set_task_duration(duration) - def calculate_seconds_per_workday(self): + def calculate_seconds_per_workday(self) -> float: def get_work_schedule(task): for rel in task.HasAssignments or []: if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"): @@ -102,7 +103,7 @@ class Usecase: return get_work_schedule(rel.RelatingObject) default_seconds_per_workday = 8 * 60 * 60 - work_schedule = get_work_schedule(self.settings["task"]) + work_schedule = get_work_schedule(self.task) if not work_schedule: return default_seconds_per_workday psets = ifcopenshell.util.element.get_psets(work_schedule) @@ -115,9 +116,9 @@ class Usecase: work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"]) return work_day_duration.seconds - def calculate_max_resource_usage_duration(self): + def calculate_max_resource_usage_duration(self) -> float: max_duration = 0 - for rel in self.settings["task"].OperatesOn or []: + for rel in self.task.OperatesOn or []: for related_object in rel.RelatedObjects: if related_object.is_a("IfcConstructionResource"): duration = self.calculate_duration_in_days(related_object) @@ -125,7 +126,7 @@ class Usecase: max_duration = duration return max_duration - def calculate_duration_in_days(self, resource): + def calculate_duration_in_days(self, resource: ifcopenshell.entity_instance) -> Union[float, None]: def is_hourly_work(schedule_work): return "T" in schedule_work @@ -140,7 +141,7 @@ class Usecase: 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.sequence.add_task_time(self.file, task=self.settings["task"]) - self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D" + def set_task_duration(self, duration: float) -> None: + if not (task_time := self.task.TaskTime): + ifcopenshell.api.sequence.add_task_time(self.file, task=self.task) + task_time.ScheduleDuration = f"P{duration}D" diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py index d0670b76e8..a6637c7a88 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py @@ -24,7 +24,7 @@ import ifcopenshell.guid import ifcopenshell.util.element import ifcopenshell.util.sequence import ifcopenshell.util.system -from typing import Optional +from typing import Optional, Union def create_baseline( @@ -44,11 +44,8 @@ def create_baseline( * Same Resource Relationships :param work_schedule: The planned work_schedule to baseline - :type work_schedule: ifcopenshell.entity_instance :param name: baseline work schedule name - :type name: str, optional :return: The baseline work_schedule - :rtype: ifcopenshell.entity_instance Example: @@ -62,23 +59,20 @@ def create_baseline( """ usecase = Usecase() usecase.file = file - usecase.settings = {"work_schedule": work_schedule, "name": name} - return usecase.execute() + return usecase.execute(work_schedule, name) class Usecase: - def execute(self): - result = self.create_baseline_work_schedule(self.settings["work_schedule"]) - return result + file: ifcopenshell.file - def create_baseline_work_schedule(self, work_schedule): + def execute(self, work_schedule: ifcopenshell.entity_instance, name: Union[str, None]) -> None: # create work schedule if not work_schedule.PredefinedType == "PLANNED": return baseline_work_schedule = ifcopenshell.api.sequence.add_work_schedule( self.file, name=work_schedule.Name, predefined_type="BASELINE" ) - baseline_work_schedule.Name = self.settings["name"] + baseline_work_schedule.Name = name self.create_baseline_reference(work_schedule, baseline_work_schedule) for summary_task in ifcopenshell.util.sequence.get_root_tasks(work_schedule): current, duplicate = ifcopenshell.api.sequence.duplicate_task(self.file, task=summary_task) @@ -88,7 +82,9 @@ class Usecase: for i, task in enumerate(current): self.create_baseline_reference(task, duplicate[i]) - def create_baseline_reference(self, relating_object, related_object): + def create_baseline_reference( + self, relating_object: ifcopenshell.entity_instance, related_object: ifcopenshell.entity_instance + ) -> ifcopenshell.entity_instance: referenced_by = None if relating_object.Declares: referenced_by = relating_object.Declares[0] diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py index db47ccd5ec..7f974bb84e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py @@ -23,9 +23,12 @@ import ifcopenshell.api.owner import ifcopenshell.api.sequence import ifcopenshell.util.element import ifcopenshell.util.sequence +from typing import Union, Any -def duplicate_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: +def duplicate_task( + file: ifcopenshell.file, task: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]: """Duplicates a task in the project The following relationships are also duplicated: @@ -35,9 +38,7 @@ def duplicate_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) * 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 @@ -55,6 +56,9 @@ def duplicate_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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 d3c8a4418a..d55bf0e4b5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py @@ -28,11 +28,8 @@ def edit_lag_time(file: ifcopenshell.file, lag_time: ifcopenshell.entity_instanc 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 :return: None - :rtype: None Example: @@ -75,14 +72,12 @@ def edit_lag_time(file: ifcopenshell.file, lag_time: ifcopenshell.entity_instanc # Or, let's make it 2 days instead. ifcopenshell.api.sequence.edit_lag_time(model, lag_time=lag, attributes={"LagValue": "P2D"}) """ - settings = {"lag_time": lag_time, "attributes": attributes} - - for name, value in settings["attributes"].items(): + for name, value in 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")]: + setattr(lag_time, name, value) + for rel in [r for r in file.get_inverse(lag_time) if r.is_a("IfcRelSequence")]: ifcopenshell.api.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 6b165f716a..519c5cdf25 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -30,11 +30,8 @@ def edit_recurrence_pattern( 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 :return: None - :rtype: None Example: @@ -55,13 +52,8 @@ def edit_recurrence_pattern( ifcopenshell.api.sequence.edit_recurrence_pattern(model, recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) """ - settings = { - "recurrence_pattern": recurrence_pattern, - "attributes": attributes, - } - - for name, value in settings["attributes"].items(): - setattr(settings["recurrence_pattern"], name, value) + for name, value in attributes.items(): + setattr(recurrence_pattern, name, value) 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 dee8a32e64..2686ec5ace 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py @@ -30,11 +30,8 @@ def edit_sequence( 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 :return: None - :rtype: None Example: @@ -62,9 +59,7 @@ def edit_sequence( ifcopenshell.api.sequence.edit_sequence(model, rel_sequence=sequence, attributes={"SequenceType": "START_START"}) """ - settings = {"rel_sequence": rel_sequence, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["rel_sequence"], name, value) - if "SequenceType" in settings["attributes"].keys(): - ifcopenshell.api.sequence.cascade_schedule(file, task=settings["rel_sequence"].RelatedProcess) + for name, value in attributes.items(): + setattr(rel_sequence, name, value) + if "SequenceType" in attributes.keys(): + ifcopenshell.api.sequence.cascade_schedule(file, task=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 dcd218059f..3fa1b88caf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py @@ -26,11 +26,8 @@ def edit_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance, attri 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 :return: None - :rtype: None Example: @@ -48,7 +45,5 @@ def edit_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance, attri # Change the identification ifcopenshell.api.sequence.edit_task(model, task=task, attributes={"Identification": "M"}) """ - settings = {"task": task, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["task"], name, value) + for name, value in attributes.items(): + setattr(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 6d9d3404d1..119ab82a14 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py @@ -36,11 +36,8 @@ def edit_task_time( 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 :return: None - :rtype: None Example: @@ -61,94 +58,89 @@ def edit_task_time( """ usecase = Usecase() usecase.file = file - usecase.settings = {"task_time": task_time, "attributes": attributes} - return usecase.execute() + return usecase.execute(task_time, attributes) class Usecase: - def execute(self): + file: ifcopenshell.file + + def execute(self, task_time: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None: + self.task_time = task_time self.task = self.get_task() self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task) # If the user specifies both an end date and a duration, the duration takes priority - if ( - self.settings["attributes"].get("ScheduleDuration", None) - and "ScheduleFinish" in self.settings["attributes"].keys() - ): - del self.settings["attributes"]["ScheduleFinish"] + if attributes.get("ScheduleDuration", None) and "ScheduleFinish" in attributes.keys(): + del attributes["ScheduleFinish"] - duration_type = self.settings["attributes"].get("DurationType", self.settings["task_time"].DurationType) - finish = self.settings["attributes"].get("ScheduleFinish", None) + duration_type = attributes.get("DurationType", self.task_time.DurationType) + finish = attributes.get("ScheduleFinish", None) if finish: if isinstance(finish, str): finish = datetime.datetime.fromisoformat(finish) - self.settings["attributes"]["ScheduleFinish"] = datetime.datetime.combine( + attributes["ScheduleFinish"] = datetime.datetime.combine( ifcopenshell.util.sequence.get_soonest_working_day(finish, duration_type, self.calendar), datetime.time(17), ) - start = self.settings["attributes"].get("ScheduleStart", None) + start = attributes.get("ScheduleStart", None) if start: if isinstance(start, str): start = datetime.datetime.fromisoformat(start) - self.settings["attributes"]["ScheduleStart"] = datetime.datetime.combine( + attributes["ScheduleStart"] = datetime.datetime.combine( ifcopenshell.util.sequence.get_soonest_working_day(start, duration_type, self.calendar), datetime.time(9), ) - for name, value in self.settings["attributes"].items(): + for name, value in attributes.items(): 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": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["task_time"], name, value) + setattr(self.task_time, name, value) - if ( - "ScheduleDuration" in self.settings["attributes"].keys() - and self.settings["task_time"].ScheduleDuration - and self.settings["task_time"].ScheduleStart - ): + if "ScheduleDuration" in attributes.keys() and task_time.ScheduleDuration and task_time.ScheduleStart: self.calculate_finish() - elif self.settings["attributes"].get("ScheduleStart", None) and self.settings["task_time"].ScheduleDuration: + elif attributes.get("ScheduleStart", None) and task_time.ScheduleDuration: self.calculate_finish() - elif self.settings["attributes"].get("ScheduleFinish", None) and self.settings["task_time"].ScheduleStart: + elif attributes.get("ScheduleFinish", None) and task_time.ScheduleStart: self.calculate_duration() - if self.settings["task_time"].ScheduleDuration and ( - "ScheduleStart" in self.settings["attributes"].keys() - or "ScheduleFinish" in self.settings["attributes"].keys() - or "ScheduleDuration" in self.settings["attributes"].keys() + if task_time.ScheduleDuration and ( + "ScheduleStart" in attributes.keys() + or "ScheduleFinish" in attributes.keys() + or "ScheduleDuration" in attributes.keys() ): ifcopenshell.api.sequence.cascade_schedule(self.file, task=self.task) - if self.settings["task_time"].ScheduleDuration: + if task_time.ScheduleDuration: self.handle_resource_calculation() 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), - self.settings["task_time"].DurationType, + ifcopenshell.util.date.ifc2datetime(self.task_time.ScheduleStart), + ifcopenshell.util.date.ifc2datetime(self.task_time.ScheduleDuration), + self.task_time.DurationType, self.calendar, date_type="FINISH", ) - self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime") + self.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.task_time.ScheduleStart) + finish = ifcopenshell.util.date.ifc2datetime(self.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.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.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.task_time) if e.is_a("IfcTask")) def handle_resource_calculation(self): resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False) 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 f82f53e534..83e56a4372 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py @@ -28,11 +28,8 @@ def edit_work_calendar( 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 :return: None - :rtype: None Example: @@ -45,7 +42,5 @@ def edit_work_calendar( ifcopenshell.api.sequence.edit_work_calendar(model, work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"}) """ - settings = {"work_calendar": work_calendar, "attributes": attributes} - - for name, value in settings["attributes"].items(): - setattr(settings["work_calendar"], name, value) + for name, value in attributes.items(): + setattr(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 920e2c671e..145460e11c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py @@ -29,11 +29,8 @@ def edit_work_plan( 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 :return: None - :rtype: None Example: @@ -46,12 +43,10 @@ def edit_work_plan( ifcopenshell.api.sequence.edit_work_plan(model, work_plan=work_plan, attributes={"Description": "Construction of phase 1"}) """ - settings = {"work_plan": work_plan, "attributes": attributes} - - for name, value in settings["attributes"].items(): + for name, value in 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) + setattr(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 5ed392ddff..98905b9a1f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py @@ -29,11 +29,8 @@ def edit_work_schedule( 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 :return: None - :rtype: None Example: @@ -50,12 +47,10 @@ def edit_work_schedule( ifcopenshell.api.sequence.edit_work_schedule(model, work_schedule=work_schedule, attributes={"Description": "3 crane design option"}) """ - settings = {"work_schedule": work_schedule, "attributes": attributes} - - for name, value in settings["attributes"].items(): + for name, value in 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) + setattr(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 07c1f818d5..7fae8b4c18 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -31,11 +31,8 @@ def edit_work_time( 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 :return: None - :rtype: None Example: @@ -54,16 +51,14 @@ def edit_work_time( ifcopenshell.api.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} - - for name, value in settings["attributes"].items(): + for name, value in attributes.items(): if name in ("Start", "StartDate"): value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") # 4 IfcWorktime Start - settings["work_time"][4] = value + work_time[4] = value elif name in ("Finish", "FinishDate"): value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") # 5 IfcWorktime Finish - settings["work_time"][5] = value + work_time[5] = value else: - setattr(settings["work_time"], name, value) + setattr(work_time, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py index a7fb9d01e6..14e82822e3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py @@ -35,9 +35,7 @@ def recalculate_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en error. :param work_schedule: The IfcWorkSchedule to perform the calculation on. - :type work_schedule: ifcopenshell.entity_instance :return: None - :rtype: None Example: @@ -50,12 +48,14 @@ def recalculate_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en """ usecase = Usecase() usecase.file = file - usecase.settings = {"work_schedule": work_schedule} - return usecase.execute() + return usecase.execute(work_schedule) class Usecase: - def execute(self): + file: ifcopenshell.file + + def execute(self, work_schedule: ifcopenshell.entity_instance) -> None: + self.work_schedule = work_schedule # The method implemented is the same as shown here: # https://www.youtube.com/watch?v=qTErIV6OqLg self.start_dates = [] @@ -88,7 +88,6 @@ class Usecase: if is_cyclic: raise RecursionError("Task graph is cyclic and so critical path method cannot be performed.") - return self.pending_nodes = set(self.g.nodes) while self.pending_nodes: @@ -100,7 +99,7 @@ class Usecase: self.update_task_times() - def build_network_graph(self): + def build_network_graph(self) -> None: self.sequence_type_map = { None: "FS", "START_START": "SS", @@ -114,14 +113,14 @@ class Usecase: 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) - for rel in self.settings["work_schedule"].Controls: + for rel in self.work_schedule.Controls: for related_object in rel.RelatedObjects: if not related_object.is_a("IfcTask"): continue self.add_node(related_object) self.g.add_edges_from(self.edges) - def add_node(self, task): + def add_node(self, task: ifcopenshell.entity_instance) -> None: if task.IsNestedBy: for rel in task.IsNestedBy: [self.add_node(o) for o in rel.RelatedObjects] @@ -176,7 +175,7 @@ class Usecase: if not successor_types: self.edges.append((task.id(), "finish", {"lag_time": 0, "type": "FF"})) - def update_task_times(self): + def update_task_times(self) -> None: for ifc_definition_id in self.g.nodes: if ifc_definition_id in ("start", "finish"): continue @@ -198,12 +197,12 @@ class Usecase: }, ) - def offset_date(self, date, days, node): + def offset_date(self, date: datetime.datetime, days: int, node: dict) -> datetime.datetime: return ifcopenshell.util.sequence.offset_date( date, datetime.timedelta(days=days), node["duration_type"], node["calendar"] ) - def forward_pass(self, node): + def forward_pass(self, node) -> bool: successors = self.g.successors(node) predecessors = list(self.g.predecessors(node)) data = self.g.nodes[node] @@ -326,7 +325,7 @@ class Usecase: return True - def backward_pass(self, node): + def backward_pass(self, node) -> bool: successors = list(self.g.successors(node)) predecessors = self.g.predecessors(node) data = self.g.nodes[node] @@ -496,12 +495,12 @@ class Usecase: def calculate_free_float( self, - predecessor_date, - successor_date, - lag_time, - predecessor_data, - successor_data, - ): + predecessor_date: datetime.datetime, + successor_date: datetime.datetime, + lag_time: int, + predecessor_data: dict, + successor_data: dict, + ) -> datetime.timedelta: if not lag_time: min_successor_date = successor_date else: diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index bb31cd7c7d..abd66e7fa8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -29,12 +29,10 @@ def dereference_structure( """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 Example: @@ -68,14 +66,12 @@ def dereference_structure( # Actually, it only goes up to storey 2. ifcopenshell.api.spatial.dereference_structure(model, products=[column], relating_structure=storey3) """ - settings = {"products": products, "relating_structure": relating_structure} - - products = set(settings["products"]) - for rel in settings["relating_structure"].ReferencesElements: + products_set = set(products) + for rel in relating_structure.ReferencesElements: related_elements = set(rel.RelatedElements) - if not related_elements.intersection(products): + if not related_elements.intersection(products_set): continue - related_elements = related_elements - products + related_elements = related_elements - products_set if related_elements: rel.RelatedElements = list(related_elements) ifcopenshell.api.owner.update_owner_history(file, **{"element": rel}) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index 2897219e68..41452244d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -46,14 +46,11 @@ def reference_structure( 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] Example: @@ -85,19 +82,16 @@ def reference_structure( model, products=[column], relating_structure=[storey2, storey3] ) """ - settings = { - "products": products, - "relating_structure": relating_structure, - } - structure = settings["relating_structure"] - products = set(settings["products"]) + structure = relating_structure + products_set = set(products) - if not products: + if not products_set: return referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure) - products_to_assign = products - referenced + products_to_assign = products_set - referenced + rel: Union[ifcopenshell.entity_instance, None] rel = next(iter(structure.ReferencesElements), None) if not products_to_assign: 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 4b7d94a4a2..65c03eb238 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 @@ -28,14 +28,8 @@ def edit_structural_analysis_model( 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 :return: None - :rtype: None """ - settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["structural_analysis_model"], name, value) - return settings["structural_analysis_model"] + for name, value in attributes.items(): + setattr(structural_analysis_model, name, value) 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 54963415df..3d33e4e8ad 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 @@ -28,19 +28,14 @@ def edit_structural_boundary_condition( 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 :return: None - :rtype: None """ - settings = {"condition": condition, "attributes": attributes} - - for name, data in settings["attributes"].items(): + for name, data in 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) + setattr(condition, name, value) 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 8b90457a47..1b7105d95d 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 @@ -16,25 +16,21 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell +from ifcopenshell.util.shape_builder import VectorType, ifc_safe_vector_type def edit_structural_item_axis( file: ifcopenshell.file, structural_item: ifcopenshell.entity_instance, - axis: tuple[float, float, float] = (0.0, 0.0, 1.0), + axis: VectorType = (0.0, 0.0, 1.0), ) -> 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: tuple[float, float, float] :return: None - :rtype: None """ - settings = {"structural_item": structural_item, "axis": 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"]) + if len(file.get_inverse(axis_dir := structural_item.Axis)) == 1: + file.remove(axis_dir) + structural_item.Axis = file.create_entity("IfcDirection", ifc_safe_vector_type(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 20298d1c0f..faa218a7a3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py @@ -28,13 +28,8 @@ def edit_structural_load( 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 :return: None - :rtype: None """ - settings = {"structural_load": structural_load, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["structural_load"], name, value) + for name, value in attributes.items(): + setattr(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 59231fb9e4..02948c637d 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 @@ -28,13 +28,8 @@ def edit_structural_load_case( 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 :return: None - :rtype: None """ - settings = {"load_case": load_case, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["load_case"], name, value) + for name, value in attributes.items(): + setattr(load_case, name, value) 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 408f8e7906..64d38cfd2f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py @@ -82,17 +82,13 @@ def add_surface_style( :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: 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 3ecc91177f..f870a3006e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py @@ -19,6 +19,7 @@ import ifcopenshell import ifcopenshell.api.style import ifcopenshell.util.element +from typing import Any def assign_material_style( @@ -115,6 +116,9 @@ def assign_material_style( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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/edit_presentation_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py index d1a42e5d9e..7a39629528 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py @@ -28,11 +28,8 @@ def edit_presentation_style( 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 :return: None - :rtype: None Example: @@ -44,7 +41,5 @@ def edit_presentation_style( # Change the name of the style to "Foo" ifcopenshell.api.style.edit_presentation_style(model, style=style, attributes={"Name": "Foo"}) """ - settings = {"style": style, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["style"], name, value) + for name, value in attributes.items(): + setattr(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 c5be1d757f..40f4fcedf4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell -from typing import Any +from typing import Any, Union def edit_surface_style( @@ -36,11 +36,8 @@ def edit_surface_style( 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 :return: None - :rtype: None Example: @@ -74,14 +71,18 @@ def edit_surface_style( """ usecase = Usecase() usecase.file = file - usecase.settings = {"style": style, "attributes": attributes or {}} - return usecase.execute() + return usecase.execute(style, attributes) class Usecase: - def execute(self): + file: ifcopenshell.file + settings: dict[str, Any] + + def execute(self, style: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None: + self.style = style + attributes = {} - for attribute in self.settings["style"].wrapped_data.declaration().as_entity().all_attributes(): + for attribute in style.wrapped_data.declaration().as_entity().all_attributes(): attribute_type = attribute.type_of_attribute() if attribute_type.as_aggregation_type() is None: attribute_type = attribute_type.declared_type().name() @@ -90,7 +91,7 @@ class Usecase: attribute_type = attribute_type.type_of_element() attributes[attribute.name()] = attribute_type - for key, value in self.settings["attributes"].items(): + for key, value in attributes.items(): attribute_class = attributes.get(key) if attribute_class == "IfcColourRgb": self.edit_colour_rgb(key, value) @@ -101,39 +102,37 @@ class Usecase: else: setattr(self.settings["style"], key, value) - def edit_colour_rgb(self, name, value: dict): - if (attribute := getattr(self.settings["style"], name)) is None: + def edit_colour_rgb(self, name: str, value: dict[str, Any]): + if (attribute := getattr(self.style, name)) is None: attribute = self.file.createIfcColourRgb() - setattr(self.settings["style"], name, attribute) + setattr(self.style, name, attribute) attribute.Name = value.get("Name", None) attribute.Red = value["Red"] attribute.Green = value["Green"] attribute.Blue = value["Blue"] - def edit_colour_or_factor(self, name, value): + def edit_colour_or_factor(self, name: str, value: Union[dict[str, Any], ifcopenshell.entity_instance, None]): if isinstance(value, dict): - attribute = getattr(self.settings["style"], name) + attribute = getattr(self.style, name) if not attribute or not attribute.is_a("IfcColourRgb"): colour = self.file.createIfcColourRgb(None, 0, 0, 0) - setattr(self.settings["style"], name, colour) - attribute = getattr(self.settings["style"], name) + setattr(self.style, name, colour) + attribute = getattr(self.style, name) attribute[1] = value["Red"] attribute[2] = value["Green"] attribute[3] = value["Blue"] else: # assume it's float value for IfcNormalisedRatioMeasure or None - existing_value = getattr(self.settings["style"], name) + existing_value = getattr(self.style, name) if existing_value and existing_value.id(): self.file.remove(existing_value) if value is not None: - value = self.file.createIfcNormalisedRatioMeasure(value) - setattr(self.settings["style"], name, value) + value = self.file.create_entity("IfcNormalisedRatioMeasure", value) + setattr(self.style, name, value) - def edit_specular_highlight(self, value): + def edit_specular_highlight(self, value: Union[dict[str, Any], None]) -> None: if value is None: - self.settings["style"].SpecularHighlight = None + self.style.SpecularHighlight = None elif value.get("IfcSpecularExponent", None): - self.settings["style"].SpecularHighlight = self.file.createIfcSpecularExponent(value["IfcSpecularExponent"]) + self.style.SpecularHighlight = self.file.createIfcSpecularExponent(value["IfcSpecularExponent"]) elif value.get("IfcSpecularRoughness", None): - self.settings["style"].SpecularHighlight = self.file.createIfcSpecularRoughness( - value["IfcSpecularRoughness"] - ) + self.style.SpecularHighlight = self.file.createIfcSpecularRoughness(value["IfcSpecularRoughness"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py index 3ee4cbf6d5..c72cca657e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py @@ -41,7 +41,6 @@ def remove_style(file: ifcopenshell.file, style: ifcopenshell.entity_instance) - """ usecase = Usecase() usecase.file = file - usecase.settings = {"style": style} return usecase.execute(style) 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 bad29c878f..19bb9e1b79 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py @@ -24,9 +24,7 @@ def remove_surface_style(file: ifcopenshell.file, style: ifcopenshell.entity_ins """Removes a presentation item from a presentation style :param style: The IfcPresentationItem to remove. - :type style: ifcopenshell.entity_instance :return: None - :rtype: None Example: 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 f56fad763c..4141adbacc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import ifcopenshell +from typing import Any def unassign_representation_styles( @@ -63,6 +64,9 @@ def unassign_representation_styles( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): if not self.settings["styles"]: return [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py index 9a1bc76f43..b0b39cfd2b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py @@ -20,7 +20,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.guid import ifcopenshell.util.element -from typing import Optional +from typing import Optional, Any def connect_port( @@ -110,6 +110,9 @@ def connect_port( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + 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/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py index 00311b7ac0..c90894ec7f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py @@ -26,11 +26,8 @@ def edit_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance, a 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 :return: None - :rtype: None Example: @@ -42,8 +39,5 @@ def edit_system(file: ifcopenshell.file, system: ifcopenshell.entity_instance, a # Change the name of the system to "HW" for Hot Water ifcopenshell.api.system.edit_system(model, system=system, attributes={"Name": "HW"}) """ - - settings = {"system": system, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["system"], name, value) + for name, value in attributes.items(): + setattr(system, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py index 1bf459920f..b5b69e276c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py @@ -19,6 +19,7 @@ import ifcopenshell import ifcopenshell.api.owner import ifcopenshell.util.element +from typing import Any def unassign_port( @@ -62,6 +63,9 @@ def unassign_port( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): if self.file.schema == "IFC2X3": return self.execute_ifc2x3() diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index 2a20f57f2f..f4bfae022e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -22,7 +22,7 @@ import ifcopenshell.api.owner import ifcopenshell.api.material import ifcopenshell.guid import ifcopenshell.util.element -from typing import Union, Iterable +from typing import Union, Iterable, Any def assign_type( @@ -186,6 +186,9 @@ def assign_type( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): if not self.settings["related_objects"]: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_derived_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_derived_unit.py index 8a68e5727f..0fba187c75 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_derived_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_derived_unit.py @@ -46,11 +46,8 @@ def add_derived_unit( :type unit_type: str :param userdefinedtype: The user defined type in case of choosing USERDEFINED, or None for no user defined type. - :type userdefinedtype: str or None :param attributes: a dictionary of attribute names and values. - :type attributes: dict :return: The newly created IfcDerivedUnit - :rtype: ifcopenshell.entity_instance Example: @@ -69,15 +66,13 @@ def add_derived_unit( #12=IfcDerivedUnit((#10,#11),.LINEARVELOCITY.,$) """ - settings = {"unit_type": unit_type, "attributes": attributes} - derive_unit_elements = [] - for named_unit in settings["attributes"]: + for named_unit in attributes: derive_unit_elements.append( - file.create_entity("IfcDerivedUnitElement", Unit=named_unit, Exponent=settings["attributes"][named_unit]) + file.create_entity("IfcDerivedUnitElement", Unit=named_unit, Exponent=attributes[named_unit]) ) return file.create_entity( - "IfcDerivedUnit", Elements=derive_unit_elements, UnitType=settings["unit_type"], UserDefinedType=userdefinedtype + "IfcDerivedUnit", Elements=derive_unit_elements, UnitType=unit_type, UserDefinedType=userdefinedtype ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index 4e2dc3f848..d57acbd435 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -18,7 +18,7 @@ import ifcopenshell import ifcopenshell.util.unit -from typing import Optional +from typing import Optional, Any def assign_unit( @@ -75,6 +75,9 @@ def assign_unit( class Usecase: + file: ifcopenshell.file + settings: dict[str, Any] + def execute(self): # We're going to refactor this to split unit creation and assignment if 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 a8b3316dbd..5bc9bbd316 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py @@ -26,13 +26,8 @@ def edit_derived_unit(file: ifcopenshell.file, unit: ifcopenshell.entity_instanc 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 :return: None - :rtype: None """ - settings = {"unit": unit, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["unit"], name, value) + for name, value in attributes.items(): + setattr(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 4fdfe6ec29..bccc61afc0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py @@ -26,11 +26,8 @@ def edit_monetary_unit(file: ifcopenshell.file, unit: ifcopenshell.entity_instan 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 :return: None - :rtype: None Example: @@ -43,7 +40,5 @@ def edit_monetary_unit(file: ifcopenshell.file, unit: ifcopenshell.entity_instan # Ah who are we kidding ifcopenshell.api.unit.edit_monetary_unit(model, unit=zwl, attributes={"Currency": "USD"}) """ - settings = {"unit": unit, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): - setattr(settings["unit"], name, value) + for name, value in attributes.items(): + setattr(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 e439e2c476..ccd0e96d54 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py @@ -29,11 +29,8 @@ def edit_named_unit(file: ifcopenshell.file, unit: ifcopenshell.entity_instance, 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 :return: None - :rtype: None Example: @@ -45,15 +42,13 @@ def edit_named_unit(file: ifcopenshell.file, unit: ifcopenshell.entity_instance, # Uh, crates? Boxes? Whatever. ifcopenshell.api.unit.edit_named_unit(model, unit=unit, attibutes={"Name": "CRATES"}) """ - settings = {"unit": unit, "attributes": attributes or {}} - - for name, value in settings["attributes"].items(): + for name, value in attributes.items(): if name == "Dimensions": - dimensions = settings["unit"].Dimensions + dimensions = unit.Dimensions if len(file.get_inverse(dimensions)) > 1: - settings["unit"].Dimensions = file.createIfcDimensionalExponents(*value) + unit.Dimensions = file.createIfcDimensionalExponents(*value) else: for i, exponent in enumerate(value): dimensions[i] = exponent continue - setattr(settings["unit"], name, value) + setattr(unit, name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py index 17eee6d4be..8e0335b77e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py @@ -23,9 +23,7 @@ def unassign_unit(file: ifcopenshell.file, units: Optional[list[ifcopenshell.ent """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 Example: @@ -44,15 +42,12 @@ def unassign_unit(file: ifcopenshell.file, units: Optional[list[ifcopenshell.ent # Actually, we don't need areas. ifcopenshell.api.unit.unassign_unit(model, units=[area]) """ - settings = {"units": units} - - unit_assignment = file.by_type("IfcUnitAssignment") - if not unit_assignment: + unit_assignments = file.by_type("IfcUnitAssignment") + if not unit_assignments: 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 + unit_assignment = unit_assignments[0] + units_set = set(unit_assignment.Units or []) + units_set = units_set - set(units or []) + if units_set: + unit_assignment.Units = list(units_set) file.remove(unit_assignment) diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py index 04c6c2ce50..d3c94b616d 100644 --- a/src/ifcopenshell-python/ifcopenshell/file.py +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -24,6 +24,7 @@ import zipfile import functools import ifcopenshell from pathlib import Path +from typing import Any from typing import Callable from typing import Generator from typing import Optional diff --git a/src/ifcopenshell-python/ifcopenshell/util/placement.py b/src/ifcopenshell-python/ifcopenshell/util/placement.py index d6caa1b657..5f8ad154b6 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/placement.py +++ b/src/ifcopenshell-python/ifcopenshell/util/placement.py @@ -137,9 +137,7 @@ def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) -> ``get_mappeditem_transformation`` instead. :param item: The IfcCartesianTransformationOperator entity - :type item: ifcopenshell.entity_instance :return: A 4x4 numpy transformation matrix - :rtype: MatrixType """ origin = np.array(inst.LocalOrigin.Coordinates) axis1 = np.array((1.0, 0.0, 0.0))