Generate functions for all API usecases for better static code features. See #2693.

This commit is contained in:
Dion Moult
2024-05-06 14:35:39 +10:00
parent 10f894e2ea
commit d11ec67129
330 changed files with 13283 additions and 13751 deletions
@@ -22,3 +22,6 @@ One common use is spatial elements, such as how a site has multiple buildings,
and a building has multiple storeys. Another is for regular elements, such as and a building has multiple storeys. Another is for regular elements, such as
how a wall is made out of members and coverings. how a wall is made out of members and coverings.
""" """
from .assign_object import assign_object
from .unassign_object import unassign_object
@@ -23,13 +23,11 @@ import ifcopenshell.util.placement
from typing import Union from typing import Union
class Usecase: def assign_object(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
relating_object: ifcopenshell.entity_instance, relating_object: ifcopenshell.entity_instance,
): ) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns object as an aggregate to the products """Assigns object as an aggregate to the products
All physical IFC model elements must be part of a hierarchical tree All physical IFC model elements must be part of a hierarchical tree
@@ -85,18 +83,16 @@ class Usecase:
# The site has a building # The site has a building
ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element)
""" """
self.file = file settings = {
self.settings = {
"products": products, "products": products,
"relating_object": relating_object, "relating_object": relating_object,
} }
def execute(self) -> Union[ifcopenshell.entity_instance, None]: if not settings["products"]:
if not self.settings["products"]:
return return
products = set(self.settings["products"]) products = set(settings["products"])
relating_object = self.settings["relating_object"] relating_object = settings["relating_object"]
is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None)
previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() previous_aggregates_rels: set[ifcopenshell.entity_instance] = set()
@@ -126,30 +122,30 @@ class Usecase:
# can be either only aggregated or only contained at the same time # can be either only aggregated or only contained at the same time
# some product might not be able to have a container # some product might not be able to have a container
possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")]
ifcopenshell.api.run("spatial.unassign_container", self.file, products=possibly_contained_products) ifcopenshell.api.run("spatial.unassign_container", file, products=possibly_contained_products)
# unassign elements from previous aggregates # unassign elements from previous aggregates
for decomposes in previous_aggregates_rels: for decomposes in previous_aggregates_rels:
related_objects = set(decomposes.RelatedObjects) - products related_objects = set(decomposes.RelatedObjects) - products
if related_objects: if related_objects:
decomposes.RelatedObjects = list(related_objects) decomposes.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": decomposes}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": decomposes})
else: else:
history = decomposes.OwnerHistory history = decomposes.OwnerHistory
self.file.remove(decomposes) file.remove(decomposes)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
# assign elements to a new aggregate # assign elements to a new aggregate
if is_decomposed_by: if is_decomposed_by:
is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_decomposed_by})
else: else:
is_decomposed_by = self.file.create_entity( is_decomposed_by = file.create_entity(
"IfcRelAggregates", "IfcRelAggregates",
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": list(products), "RelatedObjects": list(products),
"RelatingObject": relating_object, "RelatingObject": relating_object,
} }
@@ -161,7 +157,7 @@ class Usecase:
if placement and placement.is_a("IfcLocalPlacement"): if placement and placement.is_a("IfcLocalPlacement"):
ifcopenshell.api.run( ifcopenshell.api.run(
"geometry.edit_object_placement", "geometry.edit_object_placement",
self.file, file,
product=product, product=product,
matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement),
is_si=False, is_si=False,
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_object(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None:
def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]):
"""Unassigns products from their aggregate """Unassigns products from their aggregate
A product (i.e. a smaller part of a whole) may be aggregated into zero A product (i.e. a smaller part of a whole) may be aggregated into zero
@@ -57,11 +56,9 @@ class Usecase:
# nothing is returned, relationship is removed # nothing is returned, relationship is removed
ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2]) ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2])
""" """
self.file = file settings = {"products": products}
self.settings = {"products": products}
def execute(self) -> None: products = set(settings["products"])
products = set(self.settings["products"])
rels = set( rels = set(
rel rel
for product in products for product in products
@@ -72,9 +69,9 @@ class Usecase:
related_objects = set(rel.RelatedObjects) - products related_objects = set(rel.RelatedObjects) - products
if related_objects: if related_objects:
rel.RelatedObjects = list(related_objects) rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else: else:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -15,3 +15,5 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .edit_attributes import edit_attributes
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def edit_attributes(file, product=None, attributes=None) -> None:
def __init__(self, file, product=None, attributes=None):
"""Edit the attributes of a product """Edit the attributes of a product
All IFC entities have attributes. Normally they can be edited directly, All IFC entities have attributes. Normally they can be edited directly,
@@ -44,39 +43,25 @@ class Usecase:
ifcopenshell.api.run("attribute.edit_attributes", model, ifcopenshell.api.run("attribute.edit_attributes", model,
product=element, attributes={"Name": "Waldo"}) product=element, attributes={"Name": "Waldo"})
""" """
self.file = file settings = {"product": product, "attributes": attributes or {}}
self.settings = {"product": product, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["product"], name, value)
setattr(self.settings["product"], name, value) if hasattr(settings["product"], "PredefinedType"):
if hasattr(self.settings["product"], "PredefinedType"): if hasattr(settings["product"], "ElementType"):
if hasattr(self.settings["product"], "ElementType"): if settings["product"].ElementType is None and settings["product"].PredefinedType == "USERDEFINED":
if ( settings["product"].PredefinedType = "NOTDEFINED"
self.settings["product"].ElementType is None elif settings["product"].ElementType and settings["product"].PredefinedType != "USERDEFINED":
and self.settings["product"].PredefinedType == "USERDEFINED" settings["product"].PredefinedType = "USERDEFINED"
): elif hasattr(settings["product"], "ObjectType"):
self.settings["product"].PredefinedType = "NOTDEFINED" relating_type = ifcopenshell.util.element.get_type(settings["product"])
elif (
self.settings["product"].ElementType
and self.settings["product"].PredefinedType != "USERDEFINED"
):
self.settings["product"].PredefinedType = "USERDEFINED"
elif hasattr(self.settings["product"], "ObjectType"):
relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
# Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None): if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
self.settings["product"].ObjectType = None settings["product"].ObjectType = None
self.settings["product"].PredefinedType = None settings["product"].PredefinedType = None
elif ( elif settings["product"].ObjectType is None and settings["product"].PredefinedType == "USERDEFINED":
self.settings["product"].ObjectType is None settings["product"].PredefinedType = "NOTDEFINED"
and self.settings["product"].PredefinedType == "USERDEFINED" elif settings["product"].ObjectType and settings["product"].PredefinedType != "USERDEFINED":
): settings["product"].PredefinedType = "USERDEFINED"
self.settings["product"].PredefinedType = "NOTDEFINED" if hasattr(settings["product"], "OwnerHistory"):
elif ( ifcopenshell.api.run("owner.update_owner_history", file, **{"element": settings["product"]})
self.settings["product"].ObjectType
and self.settings["product"].PredefinedType != "USERDEFINED"
):
self.settings["product"].PredefinedType = "USERDEFINED"
if hasattr(self.settings["product"], "OwnerHistory"):
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": self.settings["product"]})
@@ -19,3 +19,8 @@
"""Boundaries are primarily used for representing virtual interfaces between """Boundaries are primarily used for representing virtual interfaces between
spaces for energy analysis. spaces for energy analysis.
""" """
from .assign_connection_geometry import assign_connection_geometry
from .copy_boundary import copy_boundary
from .edit_attributes import edit_attributes
from .remove_boundary import remove_boundary
@@ -19,8 +19,16 @@
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase: def assign_connection_geometry(
def __init__(self, file, rel_space_boundary=None, outer_boundary=None, inner_boundaries=None, location=None, axis=None, ref_direction=None, unit_scale=None): file,
rel_space_boundary=None,
outer_boundary=None,
inner_boundaries=None,
location=None,
axis=None,
ref_direction=None,
unit_scale=None,
) -> None:
"""Create and assign a connection geometry to a space boundary relationship """Create and assign a connection geometry to a space boundary relationship
A space boundary may optionally have a plane that represents how that A space boundary may optionally have a plane that represents how that
@@ -71,16 +79,20 @@ class Usecase:
location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.], location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.],
) )
""" """
self.file = file usecase = Usecase()
self.rel_space_boundary = rel_space_boundary usecase.file = file
self.outer_boundary = outer_boundary usecase.rel_space_boundary = rel_space_boundary
self.inner_boundaries = inner_boundaries or () usecase.outer_boundary = outer_boundary
self.location = location usecase.inner_boundaries = inner_boundaries or ()
self.axis = axis usecase.location = location
self.ref_direction = ref_direction usecase.axis = axis
self.unit_scale = unit_scale usecase.ref_direction = ref_direction
self.ifc_vertices = [] usecase.unit_scale = unit_scale
usecase.ifc_vertices = []
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.unit_scale is None: if self.unit_scale is None:
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -19,8 +19,7 @@
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def copy_boundary(file, boundary=None) -> None:
def __init__(self, file, boundary=None):
"""Copies a space boundary """Copies a space boundary
:param boundary: The IfcRelSpaceBoundary you want to copy. :param boundary: The IfcRelSpaceBoundary you want to copy.
@@ -37,11 +36,9 @@ class Usecase:
# And now we have two # And now we have two
boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary) boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary)
""" """
self.file = file settings = {"boundary": boundary}
self.settings = {"boundary": boundary}
def execute(self): result = ifcopenshell.util.element.copy(file, settings["boundary"])
result = ifcopenshell.util.element.copy(self.file, self.settings["boundary"])
if result.ConnectionGeometry: if result.ConnectionGeometry:
result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(self.file, result.ConnectionGeometry) result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry)
return result return result
@@ -17,8 +17,14 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_attributes(
def __init__(self, file, entity=None, relating_space=None, related_building_element=None, parent_boundary=None, corresponding_boundary=None): file,
entity=None,
relating_space=None,
related_building_element=None,
parent_boundary=None,
corresponding_boundary=None,
) -> None:
"""Modify the relationships of a space boundary relationship """Modify the relationships of a space boundary relationship
Currently this function is quite minimal and offers no advantage to Currently this function is quite minimal and offers no advantage to
@@ -45,17 +51,15 @@ class Usecase:
:return: None :return: None
:rtype: None :rtype: None
""" """
self.file = file entity = entity
self.entity = entity relating_space = relating_space
self.relating_space = relating_space related_building_element = related_building_element
self.related_building_element = related_building_element parent_boundary = parent_boundary
self.parent_boundary = parent_boundary corresponding_boundary = corresponding_boundary
self.corresponding_boundary = corresponding_boundary
def execute(self): entity.RelatingSpace = relating_space
self.entity.RelatingSpace = self.relating_space entity.RelatedBuildingElement = related_building_element
self.entity.RelatedBuildingElement = self.related_building_element if hasattr(entity, "ParentBoundary"):
if hasattr(self.entity, "ParentBoundary"): entity.ParentBoundary = parent_boundary
self.entity.ParentBoundary = self.parent_boundary if hasattr(entity, "CorrespondingBoundary"):
if hasattr(self.entity, "CorrespondingBoundary"): entity.CorrespondingBoundary = corresponding_boundary
self.entity.CorrespondingBoundary = self.corresponding_boundary
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_boundary(file, boundary=None) -> None:
def __init__(self, file, boundary=None):
"""Removes a space boundary """Removes a space boundary
The relating space or related building element is untouched. Only the The relating space or related building element is untouched. Only the
@@ -41,15 +40,13 @@ class Usecase:
# Let's remove it! # Let's remove it!
ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary) ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary)
""" """
self.file = file settings = {"boundary": boundary}
self.settings = {"boundary": boundary}
def execute(self): geometry = settings["boundary"].ConnectionGeometry
geometry = self.settings["boundary"].ConnectionGeometry
if geometry: if geometry:
self.settings["boundary"].ConnectionGeometry = None settings["boundary"].ConnectionGeometry = None
ifcopenshell.util.element.remove_deep2(self.file, geometry) ifcopenshell.util.element.remove_deep2(file, geometry)
history = self.settings["boundary"].OwnerHistory history = settings["boundary"].OwnerHistory
self.file.remove(self.settings["boundary"]) file.remove(settings["boundary"])
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -15,3 +15,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_classification import add_classification
from .add_reference import add_reference
from .edit_classification import edit_classification
from .edit_reference import edit_reference
from .remove_classification import remove_classification
from .remove_reference import remove_reference
@@ -22,8 +22,9 @@ import ifcopenshell.util.date
from typing import Union from typing import Union
class Usecase: def add_classification(
def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]): file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]
) -> ifcopenshell.entity_instance:
"""Adds a new classification system to the project """Adds a new classification system to the project
External classification systems such as Uniclass or Omniclass are External classification systems such as Uniclass or Omniclass are
@@ -77,12 +78,16 @@ class Usecase:
ifcopenshell.api.run("classification.add_classification", model, ifcopenshell.api.run("classification.add_classification", model,
classification=classification) classification=classification)
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"classification": classification, "classification": classification,
} }
return usecase.execute()
def execute(self) -> ifcopenshell.entity_instance:
class Usecase:
def execute(self):
if isinstance(self.settings["classification"], str): if isinstance(self.settings["classification"], str):
classification = self.file.createIfcClassification(Name=self.settings["classification"]) classification = self.file.createIfcClassification(Name=self.settings["classification"])
self.relate_to_project(classification) self.relate_to_project(classification)
@@ -23,9 +23,7 @@ import ifcopenshell.util.schema
from typing import Optional, Union from typing import Optional, Union
class Usecase: def add_reference(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
reference: Optional[ifcopenshell.entity_instance] = None, reference: Optional[ifcopenshell.entity_instance] = None,
@@ -33,7 +31,7 @@ class Usecase:
name: Optional[str] = None, name: Optional[str] = None,
classification: Optional[ifcopenshell.entity_instance] = None, classification: Optional[ifcopenshell.entity_instance] = None,
is_lightweight=True, is_lightweight=True,
): ) -> Union[ifcopenshell.entity_instance, None]:
"""Adds a new classification reference and assigns it to the list of products """Adds a new classification reference and assigns it to the list of products
A classification reference is a single entry such as "Pr_12_23_34" that A classification reference is a single entry such as "Pr_12_23_34" that
@@ -123,8 +121,9 @@ class Usecase:
products=[wall_type], classification=classification, products=[wall_type], classification=classification,
reference=reference) reference=reference)
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"products": products, "products": products,
"reference": reference, "reference": reference,
"identification": identification, "identification": identification,
@@ -132,8 +131,11 @@ class Usecase:
"classification": classification, "classification": classification,
"is_lightweight": is_lightweight, "is_lightweight": is_lightweight,
} }
return usecase.execute()
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
class Usecase:
def execute(self):
if not self.settings["products"]: if not self.settings["products"]:
return return
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_classification(file, classification=None, attributes=None) -> None:
def __init__(self, file, classification=None, attributes=None):
"""Edits the attributes of an IfcClassification """Edits the attributes of an IfcClassification
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -40,9 +39,7 @@ class Usecase:
ifcopenshell.api.run("classification.edit_classification", model, ifcopenshell.api.run("classification.edit_classification", model,
classification=classification, attributes={"Name": "Foo"}) classification=classification, attributes={"Name": "Foo"})
""" """
self.file = file settings = {"classification": classification, "attributes": attributes or {}}
self.settings = {"classification": classification, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["classification"], name, value)
setattr(self.settings["classification"], name, value)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_reference(file, reference=None, attributes=None) -> None:
def __init__(self, file, reference=None, attributes=None):
"""Edits the attributes of an IfcClassificationReference """Edits the attributes of an IfcClassificationReference
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -40,9 +39,7 @@ class Usecase:
ifcopenshell.api.run("classification.edit_reference", model, ifcopenshell.api.run("classification.edit_reference", model,
reference=reference, attributes={"Name": "Foo"}) reference=reference, attributes={"Name": "Foo"})
""" """
self.file = file settings = {"reference": reference, "attributes": attributes or {}}
self.settings = {"reference": reference, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["reference"], name, value)
setattr(self.settings["reference"], name, value)
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_classification(file, classification=None) -> None:
def __init__(self, file, classification=None):
"""Removes an IfcClassification from the project and all references """Removes an IfcClassification from the project and all references
The classification and all of its relationships, children references, The classification and all of its relationships, children references,
@@ -41,9 +40,13 @@ class Usecase:
ifcopenshell.api.run("classification.remove_classification", model, ifcopenshell.api.run("classification.remove_classification", model,
classification=classification) classification=classification)
""" """
self.file = file usecase = Usecase()
self.settings = {"classification": classification} usecase.file = file
usecase.settings = {"classification": classification}
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
references = self.get_references(self.settings["classification"]) references = self.get_references(self.settings["classification"])
for reference in references: for reference in references:
@@ -21,13 +21,11 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_reference(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
reference: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
): ) -> None:
"""Removes a classification reference from the list of products """Removes a classification reference from the list of products
If the classification reference is no longer associated to any products, If the classification reference is no longer associated to any products,
@@ -58,13 +56,11 @@ class Usecase:
ifcopenshell.api.run("classification.remove_reference", model, ifcopenshell.api.run("classification.remove_reference", model,
reference=reference, products=[wall_type]) reference=reference, products=[wall_type])
""" """
self.file = file settings = {"reference": reference, "products": products}
self.settings = {"reference": reference, "products": products}
def execute(self) -> None: is_ifc2x3 = file.schema == "IFC2X3"
is_ifc2x3 = self.file.schema == "IFC2X3" products = set(settings["products"])
products = set(self.settings["products"]) referenced = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
products -= products.difference(referenced) products -= products.difference(referenced)
# all products are already unassigned from a reference # all products are already unassigned from a reference
@@ -73,7 +69,7 @@ class Usecase:
rooted_products: set[ifcopenshell.entity_instance] = set() rooted_products: set[ifcopenshell.entity_instance] = set()
non_rooted_products: set[ifcopenshell.entity_instance] = set() non_rooted_products: set[ifcopenshell.entity_instance] = set()
for product in self.settings["products"]: for product in settings["products"]:
if product.is_a("IfcRoot"): if product.is_a("IfcRoot"):
rooted_products.add(product) rooted_products.add(product)
else: else:
@@ -90,20 +86,19 @@ class Usecase:
reference_rels = { reference_rels = {
rel rel
for rel in reference_rels for rel in reference_rels
if rel.is_a("IfcRelAssociatesClassification") if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == settings["reference"]
and rel.RelatingClassification == self.settings["reference"]
} }
for rel in reference_rels: for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - rooted_products related_objects = set(rel.RelatedObjects) - rooted_products
if related_objects: if related_objects:
rel.RelatedObjects = list(related_objects) rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else: else:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
if non_rooted_products: if non_rooted_products:
reference_rels: set[ifcopenshell.entity_instance] = set() reference_rels: set[ifcopenshell.entity_instance] = set()
@@ -113,15 +108,15 @@ class Usecase:
rels = getattr(product, "HasExternalReference", []) rels = getattr(product, "HasExternalReference", [])
reference_rels.update(rels) reference_rels.update(rels)
reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]} reference_rels = {rel for rel in reference_rels if rel.RelatingReference == settings["reference"]}
for rel in reference_rels: for rel in reference_rels:
related_objects = set(rel.RelatedResourceObjects) - non_rooted_products related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
if related_objects: if related_objects:
rel.RelatedResourceObjects = list(related_objects) rel.RelatedResourceObjects = list(related_objects)
else: else:
self.file.remove(rel) file.remove(rel)
# TODO: we only handle lightweight classifications here # TODO: we only handle lightweight classifications here
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
if not referenced_elements: if not referenced_elements:
self.file.remove(self.settings["reference"]) file.remove(settings["reference"])
@@ -15,3 +15,13 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_metric import add_metric
from .add_metric_reference import add_metric_reference
from .add_objective import add_objective
from .assign_constraint import assign_constraint
from .edit_metric import edit_metric
from .edit_objective import edit_objective
from .remove_constraint import remove_constraint
from .remove_metric import remove_metric
from .unassign_constraint import unassign_constraint
@@ -19,8 +19,7 @@
import ifcopenshell import ifcopenshell
class Usecase: def add_metric(file, objective=None) -> None:
def __init__(self, file, objective=None):
"""Add a new metric benchmark """Add a new metric benchmark
Qualitative constraints may have a series of quantitative benchmarks Qualitative constraints may have a series of quantitative benchmarks
@@ -41,13 +40,11 @@ class Usecase:
metric = ifcopenshell.api.run("constraint.add_metric", model, metric = ifcopenshell.api.run("constraint.add_metric", model,
objective=objective) objective=objective)
""" """
self.file = file settings = {
self.settings = {
"objective": objective, "objective": objective,
} }
def execute(self): metric = file.create_entity(
metric = self.file.create_entity(
"IfcMetric", "IfcMetric",
**{ **{
"Name": "Unnamed", "Name": "Unnamed",
@@ -55,8 +52,8 @@ class Usecase:
"Benchmark": "EQUALTO", "Benchmark": "EQUALTO",
} }
) )
if self.settings["objective"]: if settings["objective"]:
benchmark_values = list(self.settings["objective"].BenchmarkValues or []) benchmark_values = list(settings["objective"].BenchmarkValues or [])
benchmark_values.append(metric) benchmark_values.append(metric)
self.settings["objective"].BenchmarkValues = benchmark_values settings["objective"].BenchmarkValues = benchmark_values
return metric return metric
@@ -18,28 +18,26 @@
import ifcopenshell import ifcopenshell
class Usecase:
def __init__(self, file, metric=None, reference_path=None): def add_metric_reference(file, metric=None, reference_path=None) -> None:
""" """
Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute" 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. Used to reference a value of an attribute of an instance through a metric objective entity.
""" """
self.file = file settings = {"metric": metric, "reference_path": reference_path}
self.settings = {"metric": metric, "reference_path": reference_path}
def execute(self): if settings["reference_path"]:
if self.settings["reference_path"]: attributes = settings["reference_path"].split(".")
attributes = self.settings["reference_path"].split(".")
references_created = [] references_created = []
for i in range(len(attributes)): for i in range(len(attributes)):
if i == 0: if i == 0:
reference = self.file.create_entity("IfcReference") reference = file.create_entity("IfcReference")
reference.AttributeIdentifier = attributes[i] reference.AttributeIdentifier = attributes[i]
self.settings["metric"].ReferencePath = reference settings["metric"].ReferencePath = reference
references_created.append(reference) references_created.append(reference)
else: else:
reference = self.file.create_entity("IfcReference") reference = file.create_entity("IfcReference")
reference.AttributeIdentifier = attributes[i] reference.AttributeIdentifier = attributes[i]
references_created[i-1].InnerReference = reference references_created[i - 1].InnerReference = reference
references_created.append(reference) references_created.append(reference)
return references_created return references_created
@@ -19,8 +19,7 @@
import ifcopenshell import ifcopenshell
class Usecase: def add_objective(file) -> None:
def __init__(self, file):
"""Add a new objective constraint """Add a new objective constraint
Parametric constraints may be defined by the user. The constraint is defined Parametric constraints may be defined by the user. The constraint is defined
@@ -43,10 +42,8 @@ class Usecase:
# Note: the objective right now is purely qualitative and for # Note: the objective right now is purely qualitative and for
# information purposes. You may wish to add quantiative metrics. # information purposes. You may wish to add quantiative metrics.
""" """
self.file = file settings = {}
self.settings = {}
def execute(self): return file.create_entity(
return self.file.create_entity(
"IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"} "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
) )
@@ -21,13 +21,11 @@ import ifcopenshell.api
from typing import Union from typing import Union
class Usecase: def assign_constraint(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance, constraint: ifcopenshell.entity_instance,
): ) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a constraint to a list of products """Assigns a constraint to a list of products
This assigns a relationship between a product and a constraint, so that This assigns a relationship between a product and a constraint, so that
@@ -47,13 +45,17 @@ class Usecase:
or `None` if `products` was an empty list. or `None` if `products` was an empty list.
:rtype: ifcopenshell.entity_instance :rtype: ifcopenshell.entity_instance
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"products": products, "products": products,
"constraint": constraint, "constraint": constraint,
} }
return usecase.execute()
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
class Usecase:
def execute(self):
products = set(self.settings["products"]) products = set(self.settings["products"])
if not products: if not products:
return return
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_metric(file, metric=None, attributes=None) -> None:
def __init__(self, file, metric=None, attributes=None):
"""Edit the attributes of a metric """Edit the attributes of a metric
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -41,9 +40,7 @@ class Usecase:
ifcopenshell.api.run("constraint.edit_metric", model, ifcopenshell.api.run("constraint.edit_metric", model,
metric=metric, attributes={"ConstraintGrade": "HARD"}) metric=metric, attributes={"ConstraintGrade": "HARD"})
""" """
self.file = file settings = {"metric": metric, "attributes": attributes or {}}
self.settings = {"metric": metric, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["metric"], name, value)
setattr(self.settings["metric"], name, value)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_objective(file, objective=None, attributes=None) -> None:
def __init__(self, file, objective=None, attributes=None):
"""Edit the attributes of a objective """Edit the attributes of a objective
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -39,9 +38,7 @@ class Usecase:
ifcopenshell.api.run("constraint.edit_objective", model, ifcopenshell.api.run("constraint.edit_objective", model,
objective=objective, attributes={"ConstraintGrade": "HARD"}) objective=objective, attributes={"ConstraintGrade": "HARD"})
""" """
self.file = file settings = {"objective": objective, "attributes": attributes or {}}
self.settings = {"objective": objective, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["objective"], name, value)
setattr(self.settings["objective"], name, value)
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_constraint(file, constraint=None) -> None:
def __init__(self, file, constraint=None):
"""Remove a constraint (typically an objective) """Remove a constraint (typically an objective)
Removes a constraint definition and all of its associations to any Removes a constraint definition and all of its associations to any
@@ -42,14 +41,12 @@ class Usecase:
ifcopenshell.api.run("constraint.remove_constraint", model, ifcopenshell.api.run("constraint.remove_constraint", model,
constraint=objective) constraint=objective)
""" """
self.file = file settings = {"constraint": constraint}
self.settings = {"constraint": constraint}
def execute(self): file.remove(settings["constraint"])
self.file.remove(self.settings["constraint"]) for rel in file.by_type("IfcRelAssociatesConstraint"):
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
if not rel.RelatingConstraint: if not rel.RelatingConstraint:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def remove_metric(file, metric=None) -> None:
def __init__(self, file, metric=None):
"""Remove a metric benchmark """Remove a metric benchmark
Removes a metric benchmark and all of its associations to any products Removes a metric benchmark and all of its associations to any products
@@ -39,9 +38,13 @@ class Usecase:
ifcopenshell.api.run("constraint.remove_metric", model, ifcopenshell.api.run("constraint.remove_metric", model,
metric=metric) metric=metric)
""" """
self.file = file usecase = Usecase()
self.settings = {"metric": metric} usecase.file = file
usecase.settings = {"metric": metric}
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.settings["metric"].ReferencePath: if self.settings["metric"].ReferencePath:
reference = self.settings["metric"].ReferencePath reference = self.settings["metric"].ReferencePath
@@ -21,13 +21,11 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_constraint(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance, constraint: ifcopenshell.entity_instance,
): ) -> None:
"""Unassigns a constraint from a list of products """Unassigns a constraint from a list of products
The constraint will not be deleted and is available to be assigned to The constraint will not be deleted and is available to be assigned to
@@ -40,12 +38,16 @@ class Usecase:
:return: None :return: None
:rtype: None :rtype: None
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"products": products, "products": products,
"constraint": constraint, "constraint": constraint,
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
products = set(self.settings["products"]) products = set(self.settings["products"])
if not products: if not products:
@@ -15,3 +15,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_context import add_context
from .edit_context import edit_context
from .remove_context import remove_context
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def add_context(file, context_type=None, context_identifier=None, target_view=None, parent=None) -> None:
def __init__(self, file, context_type=None, context_identifier=None, target_view=None, parent=None):
"""Adds a new geometric representation context """Adds a new geometric representation context
In IFC, physical objects may have zero, one, or multiple geometric In IFC, physical objects may have zero, one, or multiple geometric
@@ -171,14 +170,18 @@ class Usecase:
# Place our wall at the origin # Place our wall at the origin
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"context_type": context_type, "context_type": context_type,
"parent": parent, "parent": parent,
"context_identifier": context_identifier, "context_identifier": context_identifier,
"target_view": target_view, "target_view": target_view,
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if not self.settings["parent"]: if not self.settings["parent"]:
if self.settings["context_type"] == "Plan": if self.settings["context_type"] == "Plan":
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_context(file, context, attributes) -> None:
def __init__(self, file, context, attributes):
"""Edits the attributes of an IfcGeometricRepresentationContext """Edits the attributes of an IfcGeometricRepresentationContext
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -45,9 +44,7 @@ class Usecase:
ifcopenshell.api.run("context.edit_context", model, ifcopenshell.api.run("context.edit_context", model,
context=body, attributes={"ContextIdentifier": "Body"}) context=body, attributes={"ContextIdentifier": "Body"})
""" """
self.file = file settings = {"context": context, "attributes": attributes or {}}
self.settings = {"context": context, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["context"], name, value)
setattr(self.settings["context"], name, value)
@@ -19,8 +19,7 @@
import ifcopenshell import ifcopenshell
class Usecase: def remove_context(file, context=None) -> None:
def __init__(self, file, context=None):
"""Removes an IfcGeometricRepresentationContext """Removes an IfcGeometricRepresentationContext
Any representation geometry that is assigned to the context is also Any representation geometry that is assigned to the context is also
@@ -44,24 +43,22 @@ class Usecase:
# Let's just get rid of it completely # Let's just get rid of it completely
ifcopenshell.api.run("context.remove_context", model, context=body) ifcopenshell.api.run("context.remove_context", model, context=body)
""" """
self.file = file settings = {"context": context}
self.settings = {"context": context}
def execute(self): for subcontext in settings["context"].HasSubContexts:
for subcontext in self.settings["context"].HasSubContexts: ifcopenshell.api.run("context.remove_context", file, context=subcontext)
ifcopenshell.api.run("context.remove_context", self.file, context=subcontext)
if getattr(self.settings["context"], "ParentContext", None): if getattr(settings["context"], "ParentContext", None):
new = self.settings["context"].ParentContext new = settings["context"].ParentContext
for inverse in self.file.get_inverse(self.settings["context"]): for inverse in file.get_inverse(settings["context"]):
if inverse.is_a("IfcCoordinateOperation"): if inverse.is_a("IfcCoordinateOperation"):
inverse.SourceCRS = inverse.TargetCRS inverse.SourceCRS = inverse.TargetCRS
ifcopenshell.util.element.remove_deep(self.file, inverse) ifcopenshell.util.element.remove_deep(file, inverse)
else: else:
ifcopenshell.util.element.replace_attribute(inverse, self.settings["context"], new) ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new)
self.file.remove(self.settings["context"]) file.remove(settings["context"])
else: else:
representations_in_context = self.settings["context"].RepresentationsInContext representations_in_context = settings["context"].RepresentationsInContext
self.file.remove(self.settings["context"]) file.remove(settings["context"])
for element in representations_in_context: for element in representations_in_context:
ifcopenshell.api.run("geometry.remove_representation", self.file, representation=element) ifcopenshell.api.run("geometry.remove_representation", file, representation=element)
@@ -15,3 +15,6 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .assign_control import assign_control
from .unassign_control import unassign_control
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
class Usecase: def assign_control(file, relating_control=None, related_object=None) -> None:
def __init__(self, file, relating_control=None, related_object=None):
"""Assigns a planning control or constraint to an object """Assigns a planning control or constraint to an object
IFC can describe concepts that control other objects. For example, a IFC can describe concepts that control other objects. For example, a
@@ -67,40 +66,35 @@ class Usecase:
ifcopenshell.api.run("control.assign_control", model, ifcopenshell.api.run("control.assign_control", model,
relating_control=cost_item, related_object=wall) relating_control=cost_item, related_object=wall)
""" """
self.file = file settings = {
self.settings = {
"relating_control": relating_control, "relating_control": relating_control,
"related_object": related_object, "related_object": related_object,
} }
def execute(self): if settings["related_object"].HasAssignments:
if self.settings["related_object"].HasAssignments: for assignment in settings["related_object"].HasAssignments:
for assignment in self.settings["related_object"].HasAssignments: if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]:
if (
assignment.is_a("IfcRelAssignsToControl")
and assignment.RelatingControl == self.settings["relating_control"]
):
return return
controls = None controls = None
if self.settings["relating_control"].Controls: if settings["relating_control"].Controls:
controls = self.settings["relating_control"].Controls[0] controls = settings["relating_control"].Controls[0]
if controls: if controls:
if self.settings["related_object"] in controls.RelatedObjects: if settings["related_object"] in controls.RelatedObjects:
return return
related_objects = set(controls.RelatedObjects) related_objects = set(controls.RelatedObjects)
related_objects.add(self.settings["related_object"]) related_objects.add(settings["related_object"])
controls.RelatedObjects = list(related_objects) controls.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": controls}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": controls})
else: else:
controls = self.file.create_entity( controls = file.create_entity(
"IfcRelAssignsToControl", "IfcRelAssignsToControl",
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [self.settings["related_object"]], "RelatedObjects": [settings["related_object"]],
"RelatingControl": self.settings["relating_control"], "RelatingControl": settings["relating_control"],
}, },
) )
return controls return controls
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_control(file, relating_control=None, related_object=None) -> None:
def __init__(self, file, relating_control=None, related_object=None):
"""Unassigns a planning control or constraint to an object """Unassigns a planning control or constraint to an object
:param relating_control: The IfcControl entity that is creating the :param relating_control: The IfcControl entity that is creating the
@@ -51,24 +50,22 @@ class Usecase:
relating_control=cost_item, related_object=wall) relating_control=cost_item, related_object=wall)
""" """
self.file = file settings = {
self.settings = {
"relating_control": relating_control, "relating_control": relating_control,
"related_object": related_object, "related_object": related_object,
} }
def execute(self): for rel in settings["related_object"].HasAssignments or []:
for rel in self.settings["related_object"].HasAssignments or []: if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]:
if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != self.settings["relating_control"]:
continue continue
if len(rel.RelatedObjects) == 1: if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
return return
related_objects = list(rel.RelatedObjects) related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["related_object"]) related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
return rel return rel
@@ -15,3 +15,23 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_cost_item import add_cost_item
from .add_cost_item_quantity import add_cost_item_quantity
from .add_cost_schedule import add_cost_schedule
from .add_cost_value import add_cost_value
from .assign_cost_item_quantity import assign_cost_item_quantity
from .assign_cost_value import assign_cost_value
from .calculate_cost_item_resource_value import calculate_cost_item_resource_value
from .copy_cost_item import copy_cost_item
from .copy_cost_item_values import copy_cost_item_values
from .edit_cost_item import edit_cost_item
from .edit_cost_item_quantity import edit_cost_item_quantity
from .edit_cost_schedule import edit_cost_schedule
from .edit_cost_value import edit_cost_value
from .edit_cost_value_formula import edit_cost_value_formula
from .remove_cost_item import remove_cost_item
from .remove_cost_item_quantity import remove_cost_item_quantity
from .remove_cost_schedule import remove_cost_schedule
from .remove_cost_value import remove_cost_value
from .unassign_cost_item_quantity import unassign_cost_item_quantity
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def add_cost_item(file, cost_schedule=None, cost_item=None) -> None:
def __init__(self, file, cost_schedule=None, cost_item=None):
"""Add a new cost item """Add a new cost item
A cost item represents a single line item in a cost schedule. Cost items A cost item represents a single line item in a cost schedule. Cost items
@@ -50,24 +49,22 @@ class Usecase:
# Alternatively you may add them as subitems # Alternatively you may add them as subitems
item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1) item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1)
""" """
self.file = file settings = {"cost_schedule": cost_schedule, "cost_item": cost_item}
self.settings = {"cost_schedule": cost_schedule, "cost_item": cost_item}
def execute(self): cost_item = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcCostItem")
cost_item = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCostItem")
if self.settings["cost_schedule"]: if settings["cost_schedule"]:
self.file.create_entity( file.create_entity(
"IfcRelAssignsToControl", "IfcRelAssignsToControl",
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [cost_item], "RelatedObjects": [cost_item],
"RelatingControl": self.settings["cost_schedule"], "RelatingControl": settings["cost_schedule"],
} }
) )
elif self.settings["cost_item"]: elif settings["cost_item"]:
ifcopenshell.api.run( ifcopenshell.api.run(
"nest.assign_object", self.file, related_objects=[cost_item], relating_object=self.settings["cost_item"] "nest.assign_object", file, related_objects=[cost_item], relating_object=settings["cost_item"]
) )
return cost_item return cost_item
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def add_cost_item_quantity(file, cost_item=None, ifc_class="IfcQuantityCount") -> None:
def __init__(self, file, cost_item=None, ifc_class="IfcQuantityCount"):
"""Adds a new quantity associated with a cost item """Adds a new quantity associated with a cost item
Cost items calculate their subtotal by multiplying the sum of the cost Cost items calculate their subtotal by multiplying the sum of the cost
@@ -72,20 +71,18 @@ class Usecase:
ifcopenshell.api.run("cost.add_cost_item_quantity", model, ifcopenshell.api.run("cost.add_cost_item_quantity", model,
cost_item=item, ifc_class="IfcQuantityCount") cost_item=item, ifc_class="IfcQuantityCount")
""" """
self.file = file settings = {"cost_item": cost_item, "ifc_class": ifc_class}
self.settings = {"cost_item": cost_item, "ifc_class": ifc_class}
def execute(self): quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed")
quantity[3] = 0.0 quantity[3] = 0.0
# This is a bold assumption # This is a bold assumption
# https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls: if settings["ifc_class"] == "IfcQuantityCount" and settings["cost_item"].Controls:
count = 0 count = 0
for rel in self.settings["cost_item"].Controls: for rel in settings["cost_item"].Controls:
count += len(rel.RelatedObjects) count += len(rel.RelatedObjects)
quantity[3] = count quantity[3] = count
quantities = list(self.settings["cost_item"].CostQuantities or []) quantities = list(settings["cost_item"].CostQuantities or [])
quantities.append(quantity) quantities.append(quantity)
self.settings["cost_item"].CostQuantities = quantities settings["cost_item"].CostQuantities = quantities
return quantity return quantity
@@ -21,8 +21,7 @@ import ifcopenshell.util.date
from datetime import datetime from datetime import datetime
class Usecase: def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None:
def __init__(self, file, name=None, predefined_type="NOTDEFINED"):
"""Add a new cost schedule """Add a new cost schedule
A cost schedule is a group of cost items which typically represent a A cost schedule is a group of cost items which typically represent a
@@ -53,16 +52,14 @@ class Usecase:
# Now that we have a cost schedule, we may add cost items to it # Now that we have a cost schedule, we may add cost items to it
item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
""" """
self.file = file settings = {"name": name, "predefined_type": predefined_type}
self.settings = {"name": name, "predefined_type": predefined_type}
def execute(self):
cost_schedule = ifcopenshell.api.run( cost_schedule = ifcopenshell.api.run(
"root.create_entity", "root.create_entity",
self.file, file,
ifc_class="IfcCostSchedule", ifc_class="IfcCostSchedule",
predefined_type=self.settings["predefined_type"], predefined_type=settings["predefined_type"],
name=self.settings["name"], name=settings["name"],
) )
cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
return cost_schedule return cost_schedule
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def add_cost_value(file, parent=None) -> None:
def __init__(self, file, parent=None):
"""Adds a new value or subvalue to a cost item """Adds a new value or subvalue to a cost item
A cost item's subtotal can be specified in two ways. A cost item's subtotal can be specified in two ways.
@@ -91,21 +90,19 @@ class Usecase:
ifcopenshell.api.run("cost.edit_cost_value", model, ifcopenshell.api.run("cost.edit_cost_value", model,
cost_value=subvalue2, attributes={"AppliedValue": 3.0}) cost_value=subvalue2, attributes={"AppliedValue": 3.0})
""" """
self.file = file settings = {"parent": parent}
self.settings = {"parent": parent}
def execute(self): value = file.create_entity("IfcCostValue")
value = self.file.create_entity("IfcCostValue") if settings["parent"].is_a("IfcCostItem"):
if self.settings["parent"].is_a("IfcCostItem"): values = list(settings["parent"].CostValues or [])
values = list(self.settings["parent"].CostValues or [])
values.append(value) values.append(value)
self.settings["parent"].CostValues = values settings["parent"].CostValues = values
elif self.settings["parent"].is_a("IfcConstructionResource"): elif settings["parent"].is_a("IfcConstructionResource"):
values = list(self.settings["parent"].BaseCosts or []) values = list(settings["parent"].BaseCosts or [])
values.append(value) values.append(value)
self.settings["parent"].BaseCosts = values settings["parent"].BaseCosts = values
elif self.settings["parent"].is_a("IfcCostValue"): elif settings["parent"].is_a("IfcCostValue"):
values = list(self.settings["parent"].Components or []) values = list(settings["parent"].Components or [])
values.append(value) values.append(value)
self.settings["parent"].Components = values settings["parent"].Components = values
return value return value
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def assign_cost_item_quantity(file, cost_item=None, products=None, prop_name="") -> None:
def __init__(self, file, cost_item=None, products=None, prop_name=""):
"""Adds a cost item quantity that is parametrically connected to a product """Adds a cost item quantity that is parametrically connected to a product
A cost item may have its subtotal calculated by multiplying a unit value A cost item may have its subtotal calculated by multiplying a unit value
@@ -76,25 +75,26 @@ class Usecase:
ifcopenshell.api.run("cost.assign_cost_item_quantity", model, ifcopenshell.api.run("cost.assign_cost_item_quantity", model,
cost_item=item, products=[slab], prop_name="NetVolume") cost_item=item, products=[slab], prop_name="NetVolume")
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"cost_item": cost_item, "cost_item": cost_item,
"products": products or [], "products": products or [],
"prop_name": prop_name, "prop_name": prop_name,
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.settings["prop_name"]: if self.settings["prop_name"]:
self.quantities = set(self.settings["cost_item"].CostQuantities or []) self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for product in self.settings["products"]: for product in self.settings["products"]:
self.assign_cost_control( self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"])
related_object=product, cost_item=self.settings["cost_item"]
)
if self.settings["prop_name"]: if self.settings["prop_name"]:
if ( if (
self.settings["cost_item"].CostQuantities self.settings["cost_item"].CostQuantities
and self.settings["cost_item"].CostQuantities[0].Name.lower() and self.settings["cost_item"].CostQuantities[0].Name.lower() != self.settings["prop_name"].lower()
!= self.settings["prop_name"].lower()
) or not product.is_a("IfcObject"): ) or not product.is_a("IfcObject"):
continue continue
self.add_quantity_from_related_object(product) self.add_quantity_from_related_object(product)
@@ -120,10 +120,7 @@ class Usecase:
if not qto.is_a("IfcElementQuantity"): if not qto.is_a("IfcElementQuantity"):
return return
for prop in qto.Quantities: for prop in qto.Quantities:
if ( if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
prop.is_a("IfcPhysicalSimpleQuantity")
and prop.Name.lower() == self.settings["prop_name"].lower()
):
self.quantities.add(prop) self.quantities.add(prop)
def update_cost_item_count(self): def update_cost_item_count(self):
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def assign_cost_value(file, cost_item=None, cost_rate=None) -> None:
def __init__(self, file, cost_item=None, cost_rate=None):
"""Assigns a cost value to a cost item from a schedule of rates """Assigns a cost value to a cost item from a schedule of rates
Instead of assigning cost values from scratch for each cost item in a Instead of assigning cost values from scratch for each cost item in a
@@ -60,19 +59,17 @@ class Usecase:
# Now the cost item has the same rate as the one from the schedule of rate's item # Now the cost item has the same rate as the one from the schedule of rate's item
ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate) ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate)
""" """
self.file = file settings = {"cost_item": cost_item, "cost_rate": cost_rate}
self.settings = {"cost_item": cost_item, "cost_rate": cost_rate}
def execute(self): if settings["cost_item"].CostValues:
if self.settings["cost_item"].CostValues:
[ [
ifcopenshell.api.run( ifcopenshell.api.run(
"cost.remove_cost_value", "cost.remove_cost_value",
self.file, file,
parent=self.settings["cost_item"], parent=settings["cost_item"],
cost_value=cost_value, cost_value=cost_value,
) )
for cost_value in self.settings["cost_item"].CostValues for cost_value in settings["cost_item"].CostValues
] ]
# This is an assumption, and not part of the official IFC documentation # This is an assumption, and not part of the official IFC documentation
self.settings["cost_item"].CostValues = self.settings["cost_rate"].CostValues settings["cost_item"].CostValues = settings["cost_rate"].CostValues
@@ -21,8 +21,7 @@ import ifcopenshell.util.date
import ifcopenshell.util.resource import ifcopenshell.util.resource
class Usecase: def calculate_cost_item_resource_value(file, cost_item=None) -> None:
def __init__(self, file, cost_item=None):
"""Calculates the total cost of all resources associated with a cost item """Calculates the total cost of all resources associated with a cost item
A cost item may have construction resources (e.g. equipment, material, A cost item may have construction resources (e.g. equipment, material,
@@ -84,17 +83,13 @@ class Usecase:
# (42 * 200) + 50000 = 58400 is our calculated cost # (42 * 200) + 50000 = 58400 is our calculated cost
ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item) ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item)
""" """
self.file = file settings = {"cost_item": cost_item}
self.settings = {"cost_item": cost_item}
def execute(self): for cost_value in settings["cost_item"].CostValues or []:
for cost_value in self.settings["cost_item"].CostValues or []: ifcopenshell.api.run("cost.remove_cost_value", file, parent=settings["cost_item"], cost_value=cost_value)
ifcopenshell.api.run(
"cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value
)
resources = [] resources = []
for rel in self.settings["cost_item"].Controls or []: for rel in settings["cost_item"].Controls or []:
for related_object in rel.RelatedObjects: for related_object in rel.RelatedObjects:
if related_object.is_a("IfcConstructionResource"): if related_object.is_a("IfcConstructionResource"):
resources.append(related_object) resources.append(related_object)
@@ -107,7 +102,9 @@ class Usecase:
for resource in resources: for resource in resources:
cost, unit = ifcopenshell.util.resource.get_cost(resource) cost, unit = ifcopenshell.util.resource.get_cost(resource)
if not cost: if not cost:
cost, unit = ifcopenshell.util.resource.get_parent_cost(resource) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data. cost, unit = ifcopenshell.util.resource.get_parent_cost(
resource
) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data.
quantity = ifcopenshell.util.resource.get_quantity(resource) quantity = ifcopenshell.util.resource.get_quantity(resource)
if not cost or not quantity: if not cost or not quantity:
continue continue
@@ -115,6 +112,6 @@ class Usecase:
quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
quantity = round(quantity, 2) quantity = round(quantity, 2)
formula = "{}*{}".format(cost, quantity) formula = "{}*{}".format(cost, quantity)
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"]) cost_value = ifcopenshell.api.run("cost.add_cost_value", file, parent=settings["cost_item"])
cost_value.Name = resource.Name cost_value.Name = resource.Name
ifcopenshell.api.run("cost.edit_cost_value_formula", self.file, cost_value=cost_value, formula=formula) ifcopenshell.api.run("cost.edit_cost_value_formula", file, cost_value=cost_value, formula=formula)
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def copy_cost_item(file, cost_item=None) -> None:
def __init__(self, file, cost_item=None):
"""Copies all cost items and related relationships """Copies all cost items and related relationships
The following relationships are also duplicated: The following relationships are also duplicated:
@@ -47,9 +46,13 @@ class Usecase:
""" """
self.file = file usecase = Usecase()
self.settings = {"cost_item": cost_item} usecase.file = file
usecase.settings = {"cost_item": cost_item}
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.new_cost_items = [] self.new_cost_items = []
self.duplicate_cost_item(self.settings["cost_item"]) self.duplicate_cost_item(self.settings["cost_item"])
@@ -20,8 +20,7 @@ import ifcopenshell.util.element
import ifcopenshell.api import ifcopenshell.api
class Usecase: def copy_cost_item_values(file, source=None, destination=None) -> None:
def __init__(self, file, source=None, destination=None):
"""Copies all cost values from one cost item to another """Copies all cost values from one cost item to another
Any previously existing values will be removed. The entire value is Any previously existing values will be removed. The entire value is
@@ -52,13 +51,11 @@ class Usecase:
# Let's copy the value from one item to another # Let's copy the value from one item to another
ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2) ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2)
""" """
self.file = file settings = {"source": source, "destination": destination}
self.settings = {"source": source, "destination": destination}
def execute(self): for cost_value in settings["destination"].CostValues or []:
for cost_value in self.settings["destination"].CostValues or []: ifcopenshell.api.run("cost.remove_cost_item_value", file, cost_value=cost_value)
ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=cost_value)
copied_cost_values = [] copied_cost_values = []
for cost_value in self.settings["source"].CostValues or []: for cost_value in settings["source"].CostValues or []:
copied_cost_values.append(ifcopenshell.util.element.copy_deep(self.file, cost_value)) copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value))
self.settings["destination"].CostValues = copied_cost_values settings["destination"].CostValues = copied_cost_values
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_cost_item(file, cost_item=None, attributes=None) -> None:
def __init__(self, file, cost_item=None, attributes=None):
"""Edits the attributes of an IfcCostItem """Edits the attributes of an IfcCostItem
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -39,9 +38,7 @@ class Usecase:
item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"}) ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"})
""" """
self.file = file settings = {"cost_item": cost_item, "attributes": attributes or {}}
self.settings = {"cost_item": cost_item, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["cost_item"], name, value)
setattr(self.settings["cost_item"], name, value)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> None:
def __init__(self, file, physical_quantity=None, attributes=None):
"""Edits the attributes of an IfcPhysicalQuantity """Edits the attributes of an IfcPhysicalQuantity
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -47,9 +46,7 @@ class Usecase:
ifcopenshell.api.run("cost.edit_cost_item_quantity", model, ifcopenshell.api.run("cost.edit_cost_item_quantity", model,
physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
""" """
self.file = file settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}}
self.settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["physical_quantity"], name, value)
setattr(self.settings["physical_quantity"], name, value)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None:
def __init__(self, file, cost_schedule=None, attributes=None):
"""Edits the attributes of an IfcCostSchedule """Edits the attributes of an IfcCostSchedule
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -40,9 +39,7 @@ class Usecase:
cost_schedule=schedule, attributes={"Name": "Foo"}) cost_schedule=schedule, attributes={"Name": "Foo"})
""" """
self.file = file settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}}
self.settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["cost_schedule"], name, value)
setattr(self.settings["cost_schedule"], name, value)
@@ -21,8 +21,7 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def edit_cost_value(file, cost_value=None, attributes=None) -> None:
def __init__(self, file, cost_value=None, attributes=None):
"""Edits the attributes of an IfcCostValue """Edits the attributes of an IfcCostValue
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -47,22 +46,20 @@ class Usecase:
ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
attributes={"AppliedValue": 42.0}) attributes={"AppliedValue": 42.0})
""" """
self.file = file settings = {"cost_value": cost_value, "attributes": attributes or {}}
self.settings = {"cost_value": cost_value, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items():
if name == "AppliedValue" and value is not None: if name == "AppliedValue" and value is not None:
# TODO: support all applied value select types # TODO: support all applied value select types
value = self.file.createIfcMonetaryMeasure(value) value = file.createIfcMonetaryMeasure(value)
elif name == "UnitBasis": elif name == "UnitBasis":
old_unit_basis = self.settings["cost_value"].UnitBasis old_unit_basis = settings["cost_value"].UnitBasis
if value: if value:
value_component = self.file.create_entity( value_component = file.create_entity(
ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType),
value["ValueComponent"], value["ValueComponent"],
) )
value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"]) value = file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
if old_unit_basis and len(self.file.get_inverse(old_unit_basis)) == 0: if old_unit_basis and len(file.get_inverse(old_unit_basis)) == 0:
ifcopenshell.util.element.remove_deep(self.file, old_unit_basis) ifcopenshell.util.element.remove_deep(file, old_unit_basis)
setattr(self.settings["cost_value"], name, value) setattr(settings["cost_value"], name, value)
@@ -22,8 +22,7 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def edit_cost_value_formula(file, cost_value=None, formula=None) -> None:
def __init__(self, file, cost_value=None, formula=None):
"""Sets a cost value based on a formula, similar to formulas in spreadsheets """Sets a cost value based on a formula, similar to formulas in spreadsheets
Costs may be made up of many components (e.g. labour, material, waste Costs may be made up of many components (e.g. labour, material, waste
@@ -50,9 +49,13 @@ class Usecase:
ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value, ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value,
formula="5000 * 1.19") formula="5000 * 1.19")
""" """
self.file = file usecase = Usecase()
self.settings = {"cost_value": cost_value, "formula": formula or {}} usecase.file = file
usecase.settings = {"cost_value": cost_value, "formula": formula or {}}
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
try: try:
data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"]) data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"])
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_cost_item(file, cost_item=None) -> None:
def __init__(self, file, cost_item=None):
"""Removes a cost item """Removes a cost item
All associated relationships with the cost item are also removed, All associated relationships with the cost item are also removed,
@@ -42,27 +41,25 @@ class Usecase:
item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item) ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item)
""" """
self.file = file settings = {"cost_item": cost_item}
self.settings = {"cost_item": cost_item}
def execute(self):
# TODO: do a deep purge # TODO: do a deep purge
for inverse in self.file.get_inverse(self.settings["cost_item"]): for inverse in file.get_inverse(settings["cost_item"]):
if inverse.is_a("IfcRelNests"): if inverse.is_a("IfcRelNests"):
if inverse.RelatingObject == self.settings["cost_item"]: if inverse.RelatingObject == settings["cost_item"]:
for related_object in inverse.RelatedObjects: for related_object in inverse.RelatedObjects:
ifcopenshell.api.run("cost.remove_cost_item", self.file, cost_item=related_object) ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object)
elif inverse.RelatedObjects == (self.settings["cost_item"],): elif inverse.RelatedObjects == (settings["cost_item"],):
history = inverse.OwnerHistory history = inverse.OwnerHistory
self.file.remove(inverse) file.remove(inverse)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAssignsToControl"): elif inverse.is_a("IfcRelAssignsToControl"):
history = inverse.OwnerHistory history = inverse.OwnerHistory
self.file.remove(inverse) file.remove(inverse)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
history = self.settings["cost_item"].OwnerHistory history = settings["cost_item"].OwnerHistory
self.file.remove(self.settings["cost_item"]) file.remove(settings["cost_item"])
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def remove_cost_item_quantity(file, cost_item=None, physical_quantity=None) -> None:
def __init__(self, file, cost_item=None, physical_quantity=None):
"""Removes a quantity assigned to a cost item """Removes a quantity assigned to a cost item
If the quantity is part of a product (e.g. wall), then the quantity will If the quantity is part of a product (e.g. wall), then the quantity will
@@ -44,13 +43,11 @@ class Usecase:
ifcopenshell.api.run("cost.remove_cost_item", model, ifcopenshell.api.run("cost.remove_cost_item", model,
cost_item=item, physical_quantity=quantity) cost_item=item, physical_quantity=quantity)
""" """
self.file = file settings = {"cost_item": cost_item, "physical_quantity": physical_quantity}
self.settings = {"cost_item": cost_item, "physical_quantity": physical_quantity}
def execute(self): if len(file.get_inverse(settings["physical_quantity"])) == 1:
if len(self.file.get_inverse(self.settings["physical_quantity"])) == 1: file.remove(settings["physical_quantity"])
self.file.remove(self.settings["physical_quantity"])
return return
quantities = list(self.settings["cost_item"].CostQuantities or []) quantities = list(settings["cost_item"].CostQuantities or [])
quantities.remove(self.settings["physical_quantity"]) quantities.remove(settings["physical_quantity"])
self.settings["cost_item"].CostQuantities = quantities settings["cost_item"].CostQuantities = quantities
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_cost_schedule(file, cost_schedule=None) -> None:
def __init__(self, file, cost_schedule=None):
"""Removes a cost schedule """Removes a cost schedule
All associated relationships with the cost schedule are also removed, All associated relationships with the cost schedule are also removed,
@@ -41,21 +40,17 @@ class Usecase:
item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule) ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule)
""" """
self.file = file settings = {"cost_schedule": cost_schedule}
self.settings = {"cost_schedule": cost_schedule}
def execute(self):
# TODO: do a deep purge # TODO: do a deep purge
for inverse in self.file.get_inverse(self.settings["cost_schedule"]): for inverse in file.get_inverse(settings["cost_schedule"]):
if inverse.is_a("IfcRelAssignsToControl"): if inverse.is_a("IfcRelAssignsToControl"):
[ [
ifcopenshell.api.run( ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object)
"cost.remove_cost_item", self.file, cost_item=related_object
)
for related_object in inverse.RelatedObjects for related_object in inverse.RelatedObjects
if related_object.is_a("IfcCostItem") if related_object.is_a("IfcCostItem")
] ]
history = self.settings["cost_schedule"].OwnerHistory history = settings["cost_schedule"].OwnerHistory
self.file.remove(self.settings["cost_schedule"]) file.remove(settings["cost_schedule"])
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def remove_cost_value(file, parent=None, cost_value=None) -> None:
def __init__(self, file, parent=None, cost_value=None):
"""Removes a cost value """Removes a cost value
The cost value may be assigned either to a cost item, a construction The cost value may be assigned either to a cost item, a construction
@@ -46,22 +45,20 @@ class Usecase:
ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value) ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value)
""" """
self.file = file settings = {"parent": parent, "cost_value": cost_value}
self.settings = {"parent": parent, "cost_value": cost_value}
def execute(self): if len(file.get_inverse(settings["cost_value"])) == 1:
if len(self.file.get_inverse(self.settings["cost_value"])) == 1: file.remove(settings["cost_value"])
self.file.remove(self.settings["cost_value"])
# TODO deep purge # TODO deep purge
elif self.settings["parent"].is_a("IfcCostItem"): elif settings["parent"].is_a("IfcCostItem"):
values = list(self.settings["parent"].CostValues) values = list(settings["parent"].CostValues)
values.remove(self.settings["cost_value"]) values.remove(settings["cost_value"])
self.settings["parent"].CostValues = values if values else None settings["parent"].CostValues = values if values else None
elif self.settings["parent"].is_a("IfcConstructionResource"): elif settings["parent"].is_a("IfcConstructionResource"):
values = list(self.settings["parent"].BaseCosts) values = list(settings["parent"].BaseCosts)
values.remove(self.settings["cost_value"]) values.remove(settings["cost_value"])
self.settings["parent"].BaseCosts = values if values else None settings["parent"].BaseCosts = values if values else None
elif self.settings["parent"].is_a("IfcCostValue"): elif settings["parent"].is_a("IfcCostValue"):
components = list(self.settings["parent"].Components) components = list(settings["parent"].Components)
components.remove(self.settings["cost_value"]) components.remove(settings["cost_value"])
self.settings["parent"].Components = components if components else None settings["parent"].Components = components if components else None
@@ -19,8 +19,7 @@
import ifcopenshell.api import ifcopenshell.api
class Usecase: def unassign_cost_item_quantity(file, cost_item=None, products=None) -> None:
def __init__(self, file, cost_item=None, products=None):
"""Removes quantities of a cost item that are calculated on products """Removes quantities of a cost item that are calculated on products
A cost item may have quantities that are parametrically calculated on A cost item may have quantities that are parametrically calculated on
@@ -66,9 +65,13 @@ class Usecase:
ifcopenshell.api.run("cost.unassign_cost_item_quantity", model, ifcopenshell.api.run("cost.unassign_cost_item_quantity", model,
cost_item=item, products=[slab]) cost_item=item, products=[slab])
""" """
self.file = file usecase = Usecase()
self.settings = {"cost_item": cost_item, "products": products or []} usecase.file = file
usecase.settings = {"cost_item": cost_item, "products": products or []}
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.quantities = set(self.settings["cost_item"].CostQuantities or []) self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for quantity in self.settings["cost_item"].CostQuantities or []: for quantity in self.settings["cost_item"].CostQuantities or []:
@@ -15,3 +15,12 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_information import add_information
from .add_reference import add_reference
from .assign_document import assign_document
from .edit_information import edit_information
from .edit_reference import edit_reference
from .remove_information import remove_information
from .remove_reference import remove_reference
from .unassign_document import unassign_document
@@ -19,8 +19,7 @@
import ifcopenshell import ifcopenshell
class Usecase: def add_information(file, parent=None) -> None:
def __init__(self, file, parent=None):
"""Adds a new document information to the project """Adds a new document information to the project
An IFC document information is a document associated with the project. An IFC document information is a document associated with the project.
@@ -53,22 +52,18 @@ class Usecase:
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"}) "Location": "A-GA-6100 - Overall Plan.pdf"})
""" """
self.file = file settings = {"parent": parent}
self.settings = {"parent": parent}
def execute(self): id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification"
id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification" information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"})
information = self.file.create_entity( parent = settings["parent"]
"IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"} if not parent and file.by_type("IfcProject"):
) parent = file.by_type("IfcProject")[0]
parent = self.settings["parent"]
if not parent and self.file.by_type("IfcProject"):
parent = self.file.by_type("IfcProject")[0]
if parent.is_a("IfcProject") or parent.is_a("IfcContext"): if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
self.file.create_entity( file.create_entity(
"IfcRelAssociatesDocument", "IfcRelAssociatesDocument",
GlobalId=ifcopenshell.guid.new(), GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
RelatingDocument=information, RelatingDocument=information,
RelatedObjects=[parent], RelatedObjects=[parent],
) )
@@ -79,9 +74,7 @@ class Usecase:
documents.add(information) documents.add(information)
rel.RelatedDocuments = list(documents) rel.RelatedDocuments = list(documents)
else: else:
self.file.create_entity( file.create_entity(
"IfcDocumentInformationRelationship", "IfcDocumentInformationRelationship", RelatingDocument=parent, RelatedDocuments=[information]
RelatingDocument=parent,
RelatedDocuments=[information]
) )
return information return information
@@ -19,8 +19,7 @@
import ifcopenshell import ifcopenshell
class Usecase: def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
def __init__(self, file: ifcopenshell.file, information: ifcopenshell.entity_instance):
"""Creates a new reference to a document to assign to products """Creates a new reference to a document to assign to products
A document may be associated with physical products, tasks, cost items, A document may be associated with physical products, tasks, cost items,
@@ -64,17 +63,13 @@ class Usecase:
ifcopenshell.api.run("document.edit_reference", model, ifcopenshell.api.run("document.edit_reference", model,
reference=reference2, attributes={"Identification": "2.1.15"}) reference=reference2, attributes={"Identification": "2.1.15"})
""" """
self.file = file settings = {"information": information}
self.settings = {"information": information}
def execute(self) -> ifcopenshell.entity_instance: if file.schema == "IFC2X3":
if self.file.schema == "IFC2X3": reference = file.create_entity("IfcDocumentReference", ItemReference="X")
reference = self.file.create_entity("IfcDocumentReference", ItemReference="X") if settings["information"]:
if self.settings["information"]: references = list(settings["information"].DocumentReferences or [])
references = list(self.settings["information"].DocumentReferences or [])
references.append(reference) references.append(reference)
self.settings["information"].DocumentReferences = references settings["information"].DocumentReferences = references
return reference return reference
return self.file.create_entity( return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X")
"IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X"
)
@@ -22,13 +22,11 @@ import ifcopenshell.util.element
from typing import Union from typing import Union
class Usecase: def assign_document(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance, document: ifcopenshell.entity_instance,
): ) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a document to a list of products """Assigns a document to a list of products
An object may be assigned to zero, one, or multiple documents. Almost An object may be assigned to zero, one, or multiple documents. Almost
@@ -66,49 +64,43 @@ class Usecase:
# Let's imagine storey represents an IfcBuildingStorey for the ground floor # Let's imagine storey represents an IfcBuildingStorey for the ground floor
ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
""" """
self.file = file settings = {
self.settings = {
"products": products, "products": products,
"document": document, "document": document,
} }
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.assign_reference` # NOTE: reuses code from `library.assign_reference`
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"]) referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["document"])
products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) products: set[ifcopenshell.entity_instance] = set(settings["products"])
products = products - referenced_elements products = products - referenced_elements
if not products: if not products:
return return
if self.file.schema == "IFC2X3": if file.schema == "IFC2X3":
rel = next( rel = next(
( (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == settings["document"]),
r
for r in self.file.by_type("IfcRelAssociatesDocument")
if r.RelatingDocument == self.settings["document"]
),
None, None,
) )
else: else:
ifc_class = self.settings["document"].is_a() ifc_class = settings["document"].is_a()
if ifc_class == "IfcDocumentReference": if ifc_class == "IfcDocumentReference":
rel = next(iter(self.settings["document"].DocumentRefForObjects), None) rel = next(iter(settings["document"].DocumentRefForObjects), None)
elif ifc_class == "IfcDocumentInformation": elif ifc_class == "IfcDocumentInformation":
rel = next(iter(self.settings["document"].DocumentInfoForObjects), None) rel = next(iter(settings["document"].DocumentInfoForObjects), None)
if not rel: if not rel:
return self.file.create_entity( return file.create_entity(
"IfcRelAssociatesDocument", "IfcRelAssociatesDocument",
GlobalId=ifcopenshell.guid.new(), GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
RelatedObjects=list(products), RelatedObjects=list(products),
RelatingDocument=self.settings["document"], RelatingDocument=settings["document"],
) )
related_objects = set(rel.RelatedObjects) | products related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects) rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
return rel return rel
@@ -19,13 +19,11 @@ import ifcopenshell
from typing import Any, Optional from typing import Any, Optional
class Usecase: def edit_information(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
information: ifcopenshell.entity_instance, information: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None, attributes: Optional[dict[str, Any]] = None,
): ) -> None:
"""Edits the attributes of an IfcDocumentInformation """Edits the attributes of an IfcDocumentInformation
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -48,9 +46,7 @@ class Usecase:
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"}) "Location": "A-GA-6100 - Overall Plan.pdf"})
""" """
self.file = file settings = {"information": information, "attributes": attributes or {}}
self.settings = {"information": information, "attributes": attributes or {}}
def execute(self) -> None: for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["information"], name, value)
setattr(self.settings["information"], name, value)
@@ -19,13 +19,11 @@ import ifcopenshell
from typing import Any, Optional from typing import Any, Optional
class Usecase: def edit_reference(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
reference: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None, attributes: Optional[dict[str, Any]] = None,
): ) -> None:
"""Edits the attributes of an IfcDocumentReference """Edits the attributes of an IfcDocumentReference
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -51,9 +49,7 @@ class Usecase:
ifcopenshell.api.run("document.edit_reference", model, ifcopenshell.api.run("document.edit_reference", model,
reference=reference, attributes={"Identification": "2.1.15"}) reference=reference, attributes={"Identification": "2.1.15"})
""" """
self.file = file settings = {"reference": reference, "attributes": attributes or {}}
self.settings = {"reference": reference, "attributes": attributes or {}}
def execute(self) -> None: for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["reference"], name, value)
setattr(self.settings["reference"], name, value)
@@ -22,8 +22,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_information(file, information=None) -> None:
def __init__(self, file, information=None):
"""Removes a document information """Removes a document information
All references and associations are also removed. All references and associations are also removed.
@@ -42,25 +41,23 @@ class Usecase:
# ... and remove it! # ... and remove it!
ifcopenshell.api.run("document.remove_information", model, information=document) ifcopenshell.api.run("document.remove_information", model, information=document)
""" """
self.file = file settings = {"information": information}
self.settings = {"information": information}
def execute(self): for reference in settings["information"].HasDocumentReferences or []:
for reference in self.settings["information"].HasDocumentReferences or []: ifcopenshell.api.run("document.remove_reference", file, reference=reference)
ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
for rel in self.settings["information"].IsPointer or []: for rel in settings["information"].IsPointer or []:
for information in rel.RelatedDocuments: for information in rel.RelatedDocuments:
ifcopenshell.api.run("document.remove_information", self.file, information=information) ifcopenshell.api.run("document.remove_information", file, information=information)
for rel in self.settings["information"].IsPointedTo or []: for rel in settings["information"].IsPointedTo or []:
if rel.RelatedDocuments == (self.settings["information"],): if rel.RelatedDocuments == (settings["information"],):
# This relationship is non-rooted # This relationship is non-rooted
self.file.remove(rel) file.remove(rel)
for rel in self.settings["information"].DocumentInfoForObjects or []: for rel in settings["information"].DocumentInfoForObjects or []:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
self.file.remove(self.settings["information"]) file.remove(settings["information"])
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance):
"""Remove a document reference """Remove a document reference
All associations with objects are removed. All associations with objects are removed.
@@ -39,13 +38,11 @@ class Usecase:
reference = ifcopenshell.api.run("document.add_reference", model, information=document) reference = ifcopenshell.api.run("document.add_reference", model, information=document)
ifcopenshell.api.run("document.remove_reference", model, reference=reference) ifcopenshell.api.run("document.remove_reference", model, reference=reference)
""" """
self.file = file settings = {"reference": reference}
self.settings = {"reference": reference}
def execute(self) -> None: for rel in settings["reference"].DocumentRefForObjects or []:
for rel in self.settings["reference"].DocumentRefForObjects or []:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
self.file.remove(self.settings["reference"]) file.remove(settings["reference"])
@@ -21,13 +21,11 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_document(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance], products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance, document: ifcopenshell.entity_instance,
): ) -> None:
"""Unassigns a document and an association to the list of products """Unassigns a document and an association to the list of products
:param product: The list of objects that the document reference or information is :param product: The list of objects that the document reference or information is
@@ -56,34 +54,32 @@ class Usecase:
# Now let's change our mind and remove the association # Now let's change our mind and remove the association
ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference) ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
""" """
self.file = file settings = {
self.settings = {
"products": products, "products": products,
"document": document, "document": document,
} }
def execute(self):
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.un assign_reference` # NOTE: reuses code from `library.un assign_reference`
reference_rels: set[ifcopenshell.entity_instance] = set() reference_rels: set[ifcopenshell.entity_instance] = set()
products = set(self.settings["products"]) products = set(settings["products"])
for product in products: for product in products:
reference_rels.update(product.HasAssociations) reference_rels.update(product.HasAssociations)
reference_rels = { reference_rels = {
rel rel
for rel in reference_rels for rel in reference_rels
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"] if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"]
} }
for rel in reference_rels: for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products related_objects = set(rel.RelatedObjects) - products
if related_objects: if related_objects:
rel.RelatedObjects = list(related_objects) rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else: else:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -15,3 +15,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .assign_product import assign_product
from .edit_text_literal import edit_text_literal
from .unassign_product import unassign_product
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
class Usecase: def assign_product(file, relating_product=None, related_object=None) -> None:
def __init__(self, file, relating_product=None, related_object=None):
"""Associates a product and an object, typically for annotation """Associates a product and an object, typically for annotation
Warning: this is an experimental API. Warning: this is an experimental API.
@@ -58,54 +57,52 @@ class Usecase:
ifcopenshell.api.run("drawing.assign_product", model, ifcopenshell.api.run("drawing.assign_product", model,
relating_product=furniture, related_object=annotation) relating_product=furniture, related_object=annotation)
""" """
self.file = file settings = {
self.settings = {
"relating_product": relating_product, "relating_product": relating_product,
"related_object": related_object, "related_object": related_object,
} }
def execute(self): is_grid_axis = settings["relating_product"].is_a("IfcGridAxis")
is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis")
if is_grid_axis: if is_grid_axis:
if self.settings["related_object"].HasAssignments: if settings["related_object"].HasAssignments:
for rel in self.settings["related_object"].HasAssignments: for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == self.settings["relating_product"].AxisTag: if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag:
return return
elif self.settings["related_object"].HasAssignments: elif settings["related_object"].HasAssignments:
for rel in self.settings["related_object"].HasAssignments: for rel in settings["related_object"].HasAssignments:
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == self.settings["relating_product"]: if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]:
return return
referenced_by = None referenced_by = None
if is_grid_axis: if is_grid_axis:
axis = self.settings["relating_product"] axis = settings["relating_product"]
grid = None grid = None
for attribute in ("PartOfW", "PartOfV", "PartOfU"): for attribute in ("PartOfW", "PartOfV", "PartOfU"):
if getattr(axis, attribute, None): if getattr(axis, attribute, None):
grid = getattr(axis, attribute)[0] grid = getattr(axis, attribute)[0]
self.settings["relating_product"] = grid settings["relating_product"] = grid
for rel in grid.ReferencedBy: for rel in grid.ReferencedBy:
if rel.Name == axis.AxisTag: if rel.Name == axis.AxisTag:
referenced_by = rel referenced_by = rel
break break
elif self.settings["relating_product"].ReferencedBy: elif settings["relating_product"].ReferencedBy:
referenced_by = self.settings["relating_product"].ReferencedBy[0] referenced_by = settings["relating_product"].ReferencedBy[0]
if referenced_by: if referenced_by:
related_objects = list(referenced_by.RelatedObjects) related_objects = list(referenced_by.RelatedObjects)
related_objects.append(self.settings["related_object"]) related_objects.append(settings["related_object"])
referenced_by.RelatedObjects = related_objects referenced_by.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by})
else: else:
referenced_by = self.file.create_entity( referenced_by = file.create_entity(
"IfcRelAssignsToProduct", "IfcRelAssignsToProduct",
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [self.settings["related_object"]], "RelatedObjects": [settings["related_object"]],
"RelatingProduct": self.settings["relating_product"], "RelatingProduct": settings["relating_product"],
} }
) )
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_text_literal(file, text_literal=None, attributes=None) -> None:
def __init__(self, file, text_literal=None, attributes=None):
"""Edits the attributes of an IfcTextLiteral """Edits the attributes of an IfcTextLiteral
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -39,9 +38,7 @@ class Usecase:
ifcopenshell.api.run("drawing.edit_text_literal", model, ifcopenshell.api.run("drawing.edit_text_literal", model,
text_literal=text, attributes={"Literal": "MY ANNOTATION"}) text_literal=text, attributes={"Literal": "MY ANNOTATION"})
""" """
self.file = file settings = {"text_literal": text_literal, "attributes": attributes or {}}
self.settings = {"text_literal": text_literal, "attributes": attributes or {}}
def execute(self): for name, value in settings["attributes"].items():
for name, value in self.settings["attributes"].items(): setattr(settings["text_literal"], name, value)
setattr(self.settings["text_literal"], name, value)
@@ -21,8 +21,7 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_product(file, relating_product=None, related_object=None) -> None:
def __init__(self, file, relating_product=None, related_object=None):
"""Unassigns a product and an object (typically an annotation) """Unassigns a product and an object (typically an annotation)
Smart annotation objects can be associated with products so that they Smart annotation objects can be associated with products so that they
@@ -51,24 +50,22 @@ class Usecase:
ifcopenshell.api.run("drawing.unassign_product", model, ifcopenshell.api.run("drawing.unassign_product", model,
relating_product=furniture, related_object=annotation) relating_product=furniture, related_object=annotation)
""" """
self.file = file settings = {
self.settings = {
"relating_product": relating_product, "relating_product": relating_product,
"related_object": related_object, "related_object": related_object,
} }
def execute(self): for rel in settings["related_object"].HasAssignments or []:
for rel in self.settings["related_object"].HasAssignments or []: if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]:
continue continue
if len(rel.RelatedObjects) == 1: if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory history = rel.OwnerHistory
self.file.remove(rel) file.remove(rel)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
return return
related_objects = list(rel.RelatedObjects) related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["related_object"]) related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
return rel return rel
@@ -15,3 +15,30 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_axis_representation import add_axis_representation
from .add_boolean import add_boolean
from .add_door_representation import add_door_representation
from .add_footprint_representation import add_footprint_representation
from .add_mesh_representation import add_mesh_representation
from .add_profile_representation import add_profile_representation
from .add_railing_representation import add_railing_representation
try:
from .add_representation import add_representation
except ModuleNotFoundError as e:
print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}")
from .add_slab_representation import add_slab_representation
from .add_wall_representation import add_wall_representation
from .add_window_representation import add_window_representation
from .assign_representation import assign_representation
from .connect_element import connect_element
from .connect_path import connect_path
from .create_2pt_wall import create_2pt_wall
from .disconnect_element import disconnect_element
from .disconnect_path import disconnect_path
from .edit_object_placement import edit_object_placement
from .map_representation import map_representation
from .remove_boolean import remove_boolean
from .remove_representation import remove_representation
from .unassign_representation import unassign_representation
@@ -19,8 +19,7 @@
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase: def add_axis_representation(file, context=None, axis=None) -> None:
def __init__(self, file, context=None, axis=None):
"""Adds a new axis representation """Adds a new axis representation
Certain objects are typically "axis-based", such as walls, beams, Certain objects are typically "axis-based", such as walls, beams,
@@ -68,12 +67,16 @@ class Usecase:
axis = ifcopenshell.api.run("geometry.add_axis_representation", model, axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
context=context, axis=[(0.0, 0.0), (1.0, 0.0)]) context=context, axis=[(0.0, 0.0), (1.0, 0.0)])
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"context": context, "context": context,
"axis": axis or [], "axis": axis or [],
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
is_2d = len(self.settings["axis"][0]) == 2 is_2d = len(self.settings["axis"][0]) == 2
@@ -82,9 +85,13 @@ class Usecase:
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
else: else:
if is_2d: if is_2d:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False) curve = self.file.createIfcIndexedPolyCurve(
self.file.createIfcCartesianPointList2D(points), None, False
)
else: else:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points), None, False) curve = self.file.createIfcIndexedPolyCurve(
self.file.createIfcCartesianPointList3D(points), None, False
)
return self.file.createIfcShapeRepresentation( return self.file.createIfcShapeRepresentation(
self.settings["context"], self.settings["context"],
self.settings["context"].ContextIdentifier, self.settings["context"].ContextIdentifier,
@@ -20,10 +20,10 @@ import ifcopenshell.util.unit
import numpy as np import numpy as np
class Usecase: def add_boolean(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"representation": None, "representation": None,
"operator": "DIFFERENCE", "operator": "DIFFERENCE",
# IfcHalfSpaceSolid, Mesh # IfcHalfSpaceSolid, Mesh
@@ -35,9 +35,12 @@ class Usecase:
"should_force_faceted_brep": False, "should_force_faceted_brep": False,
"should_force_triangulation": False, "should_force_triangulation": False,
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
if self.settings["type"] == "IfcHalfSpaceSolid": if self.settings["type"] == "IfcHalfSpaceSolid":
@@ -63,11 +63,7 @@ def create_ifc_door_lining(
points = [p.xz for p in points] points = [p.xz for p in points]
door_lining = builder.polyline(points, closed=True) door_lining = builder.polyline(points, closed=True)
door_lining = builder.extrude( door_lining = builder.extrude(door_lining, size.y, **builder.extrude_kwargs("Y"))
door_lining,
size.y,
**builder.extrude_kwargs("Y")
)
builder.translate(door_lining, position) builder.translate(door_lining, position)
return door_lining return door_lining
@@ -79,20 +75,20 @@ def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0,
return box return box
class Usecase: def add_door_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): """units in usecase_settings expected to be in ifc project units"""
"""units in settings expected to be in ifc project units""" usecase = Usecase()
self.file = file usecase.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
self.settings.update( usecase.settings.update(
{ {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"overall_height": self.convert_si_to_unit(2.0), "overall_height": usecase.convert_si_to_unit(2.0),
"overall_width": self.convert_si_to_unit(0.9), "overall_width": usecase.convert_si_to_unit(0.9),
# DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL, # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
# DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT, # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
# DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING, # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
@@ -103,39 +99,39 @@ class Usecase:
# SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT
"operation_type": "SINGLE_SWING_LEFT", # door type "operation_type": "SINGLE_SWING_LEFT", # door type
"lining_properties": { "lining_properties": {
"LiningDepth": self.convert_si_to_unit(0.050), "LiningDepth": usecase.convert_si_to_unit(0.050),
"LiningThickness": self.convert_si_to_unit(0.050), "LiningThickness": usecase.convert_si_to_unit(0.050),
# offset from the outer side of the wall (by Y-axis) # offset from the outer side of the wall (by Y-axis)
"LiningOffset": self.convert_si_to_unit(0.0), "LiningOffset": usecase.convert_si_to_unit(0.0),
# offset from the wall # offset from the wall
"LiningToPanelOffsetX": self.convert_si_to_unit(0.025), "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
# offset from the X-axis (unlike windows) # offset from the X-axis (unlike windows)
"LiningToPanelOffsetY": self.convert_si_to_unit(0.025), "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
# transom - vertical distance between door and window panels # transom - vertical distance between door and window panels
"TransomThickness": self.convert_si_to_unit(0.000), "TransomThickness": usecase.convert_si_to_unit(0.000),
# TransomOffset - distance from the bottom door opening # TransomOffset - distance from the bottom door opening
# to the beginning of the transom # to the beginning of the transom
# unlike windows TransomOffset which goes to the center of the transom # unlike windows TransomOffset which goes to the center of the transom
"TransomOffset": self.convert_si_to_unit(1.525), "TransomOffset": usecase.convert_si_to_unit(1.525),
"ShapeAspectStyle": None, # DEPRECATED "ShapeAspectStyle": None, # DEPRECATED
# Casing cover wall faces around the opening # Casing cover wall faces around the opening
# on the left, right and upper sides # on the left, right and upper sides
# Casing should be either on both sides of the wall or no casing # Casing should be either on both sides of the wall or no casing
# If `LiningOffset` is present then therefore casing is not possible on outer wall # If `LiningOffset` is present then therefore casing is not possible on outer wall
# therefore there will be no casing on inner wall either # therefore there will be no casing on inner wall either
"CasingDepth": self.convert_si_to_unit(0.005), "CasingDepth": usecase.convert_si_to_unit(0.005),
"CasingThickness": self.convert_si_to_unit(0.075), # by Z-axis "CasingThickness": usecase.convert_si_to_unit(0.075), # by Z-axis
# Threshold covers the bottom side of the opening # Threshold covers the bottom side of the opening
"ThresholdDepth": self.convert_si_to_unit(0.1), "ThresholdDepth": usecase.convert_si_to_unit(0.1),
"ThresholdThickness": self.convert_si_to_unit(0.025), # by Z-axis "ThresholdThickness": usecase.convert_si_to_unit(0.025), # by Z-axis
# offset by Y-axis # offset by Y-axis
"ThresholdOffset": self.convert_si_to_unit(0.000), "ThresholdOffset": usecase.convert_si_to_unit(0.000),
}, },
"panel_properties": { "panel_properties": {
"PanelDepth": self.convert_si_to_unit(0.035), # by Y "PanelDepth": usecase.convert_si_to_unit(0.035), # by Y
"PanelWidth": 1.0, # as ratio to the clear door opening "PanelWidth": 1.0, # as ratio to the clear door opening
"FrameDepth": self.convert_si_to_unit(0.035), # by Y "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
"FrameThickness": self.convert_si_to_unit(0.035), # by X "FrameThickness": usecase.convert_si_to_unit(0.035), # by X
# LEFT, MIDDLE, RIGHT, NOTDEFINED # LEFT, MIDDLE, RIGHT, NOTDEFINED
"PanelPosition": ..., # NEVER USED "PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how door panels operate # defines the basic ways to describe how door panels operate
@@ -145,9 +141,12 @@ class Usecase:
}, },
} }
) )
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
builder = ShapeBuilder(self.file) builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"] overall_height = self.settings["overall_height"]
@@ -19,20 +19,17 @@
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase: def add_footprint_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): settings = {
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"curves": [], # A list of IFC curves to include in the curve set "curves": [], # A list of IFC curves to include in the curve set
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value settings[key] = value
def execute(self): return file.createIfcShapeRepresentation(
return self.file.createIfcShapeRepresentation( settings["context"],
self.settings["context"], settings["context"].ContextIdentifier,
self.settings["context"].ContextIdentifier,
"GeometricCurveSet", "GeometricCurveSet",
[self.file.createIfcGeometricCurveSet(self.settings["curves"])], [file.createIfcGeometricCurveSet(settings["curves"])],
) )
@@ -19,10 +19,10 @@
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase: def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None:
def __init__(self, file: ifcopenshell.file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
# Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...] # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
# ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...] # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
@@ -35,9 +35,12 @@ class Usecase:
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
"force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.settings["unit_scale"] is None: if self.settings["unit_scale"] is None:
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -21,10 +21,10 @@ import ifcopenshell.util.unit
from ifcopenshell.util.data import Clipping from ifcopenshell.util.data import Clipping
class Usecase: def add_profile_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"profile": None, "profile": None,
"depth": 1.0, "depth": 1.0,
@@ -34,9 +34,12 @@ class Usecase:
"clippings": [], # A list of planes that define clipping half space solids "clippings": [], # A list of planes that define clipping half space solids
"placement_zx_axes": (None, None), "placement_zx_axes": (None, None),
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
@@ -31,39 +31,42 @@ def mm(x):
return x / 1000 return x / 1000
class Usecase: def add_railing_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings):
""" """
units in settings expected to be in ifc project units units in usecase_settings expected to be in ifc project units
`railing_path` is a list of point coordinates for the railing path, `railing_path` is a list of point coordinates for the railing path,
coordinates are expected to be at the top of the railing, not at the center coordinates are expected to be at the top of the railing, not at the center
`railing_path` is expected to be a list of Vector objects `railing_path` is expected to be a list of Vector objects
""" """
self.file = file usecase = Usecase()
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} usecase.file = file
self.settings.update( usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
usecase.settings.update(
{ {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"railing_type": "WALL_MOUNTED_HANDRAIL", "railing_type": "WALL_MOUNTED_HANDRAIL",
"railing_path": self.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]), "railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
"use_manual_supports": False, "use_manual_supports": False,
"support_spacing": self.convert_si_to_unit(mm(1000)), "support_spacing": usecase.convert_si_to_unit(mm(1000)),
"railing_diameter": self.convert_si_to_unit(mm(50)), "railing_diameter": usecase.convert_si_to_unit(mm(50)),
"clear_width": self.convert_si_to_unit(mm(40)), "clear_width": usecase.convert_si_to_unit(mm(40)),
"terminal_type": "180", "terminal_type": "180",
"height": self.convert_si_to_unit(mm(1000)), "height": usecase.convert_si_to_unit(mm(1000)),
"looped_path": False, "looped_path": False,
} }
) )
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
if self.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL": if usecase.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL":
raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.') raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
arc_points = [] arc_points = []
items_3d = [] items_3d = []
@@ -28,11 +28,11 @@ X_AXIS = Vector((1, 0, 0))
EPSILON = 1e-6 EPSILON = 1e-6
class Usecase: def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance:
def __init__(self, file: ifcopenshell.file, **settings): usecase = Usecase()
# TODO: This usecase currently depends on Blender's data model # TODO: This usecase currently depends on Blender's data model
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
@@ -54,11 +54,14 @@ class Usecase:
"profile_set_usage": None, # The material profile set if the extrusion requires it "profile_set_usage": None, # The material profile set if the extrusion requires it
"text_literal": None, # The text literal if the representation requires it "text_literal": None, # The text literal if the representation requires it
} }
self.ifc_vertices = [] usecase.ifc_vertices = []
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
def execute(self) -> ifcopenshell.entity_instance:
class Usecase:
def execute(self):
self.is_manifold = None self.is_manifold = None
if ( if (
isinstance(self.settings["geometry"], bpy.types.Mesh) isinstance(self.settings["geometry"], bpy.types.Mesh)
@@ -374,10 +377,12 @@ class Usecase:
return items return items
def create_plane(self, polygon): def create_plane(self, polygon):
return self.file.createIfcPlane(Position=self.file.createIfcAxis2Placement3D( return self.file.createIfcPlane(
Position=self.file.createIfcAxis2Placement3D(
Location=self.file.createIfcCartesianPoint(polygon.center), Location=self.file.createIfcCartesianPoint(polygon.center),
Axis=self.file.createIfcDirection(polygon.normal), Axis=self.file.createIfcDirection(polygon.normal),
)) )
)
def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]: def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]:
items = [] items = []
@@ -20,10 +20,10 @@ import ifcopenshell.util.unit
from math import sin, cos from math import sin, cos
class Usecase: def add_slab_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"depth": 0.2, "depth": 0.2,
"x_angle": 0, # Radians "x_angle": 0, # Radians
@@ -31,9 +31,12 @@ class Usecase:
# or by dictionaries of arguments for `Clipping.parse` # or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids "clippings": [], # A list of planes that define clipping half space solids
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
return self.file.createIfcShapeRepresentation( return self.file.createIfcShapeRepresentation(
@@ -21,10 +21,10 @@ from math import sin, cos
from ifcopenshell.util.data import Clipping from ifcopenshell.util.data import Clipping
class Usecase: def add_wall_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = { usecase.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
"length": 1.0, "length": 1.0,
"height": 3.0, "height": 3.0,
@@ -37,9 +37,12 @@ class Usecase:
"clippings": [], # A list of planes that define clipping half space solids "clippings": [], # A list of planes that define clipping half space solids
"booleans": [], # Any existing IfcBooleanResults "booleans": [], # Any existing IfcBooleanResults
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
@@ -56,12 +56,7 @@ def create_ifc_window_frame_simple(
th_left, th_up, th_right, th_bottom = thickness th_left, th_up, th_right, th_bottom = thickness
def get_extruded_profile(profile): def get_extruded_profile(profile):
return builder.extrude( return builder.extrude(profile, size.y, position=position, **builder.extrude_kwargs("Y"))
profile,
size.y,
position=position,
**builder.extrude_kwargs("Y")
)
# if all lining sides are present then we can just use two rectangles # if all lining sides are present then we can just use two rectangles
# as inner and outer curves of the profile # as inner and outer curves of the profile
@@ -207,12 +202,7 @@ def create_ifc_window(
glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0) glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0)
glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0]) glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0])
glass = builder.extrude( glass = builder.extrude(glass_rect, glass_thickness, position=glass_position, **builder.extrude_kwargs("Y"))
glass_rect,
glass_thickness,
position=glass_position,
**builder.extrude_kwargs("Y")
)
output_items = [lining_items, frame_extruded_items, [glass]] output_items = [lining_items, frame_extruded_items, [glass]]
builder.translate(chain(*output_items), position) builder.translate(chain(*output_items), position)
@@ -220,58 +210,58 @@ def create_ifc_window(
return output_items return output_items
class Usecase: def add_window_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): """units in usecase_settings expected to be in ifc project units"""
"""units in settings expected to be in ifc project units""" usecase = Usecase()
self.file = file usecase.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
self.settings.update( usecase.settings.update(
{ {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
# TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
# TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
"partition_type": "SINGLE_PANEL", "partition_type": "SINGLE_PANEL",
"overall_height": self.convert_si_to_unit(0.9), "overall_height": usecase.convert_si_to_unit(0.9),
"overall_width": self.convert_si_to_unit(0.6), "overall_width": usecase.convert_si_to_unit(0.6),
"lining_properties": { "lining_properties": {
"LiningDepth": self.convert_si_to_unit(0.050), "LiningDepth": usecase.convert_si_to_unit(0.050),
"LiningThickness": self.convert_si_to_unit(0.050), "LiningThickness": usecase.convert_si_to_unit(0.050),
"LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall "LiningOffset": usecase.convert_si_to_unit(0.050), # offset to the wall
# offset from the wall # offset from the wall
"LiningToPanelOffsetX": self.convert_si_to_unit(0.025), "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
# offset from the lining # offset from the lining
# that way it allows you to define overall_depth constant between all panels # that way it allows you to define overall_depth constant between all panels
# and still have panels with different size: # and still have panels with different size:
# overall_depth = lining_depth + offset_y # overall_depth = lining_depth + offset_y
# full offset from X axis = overall_depth - frame_depth # full offset from X axis = overall_depth - frame_depth
"LiningToPanelOffsetY": self.convert_si_to_unit(0.025), "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
# applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight # TriplePanelLeft, TriplePanelRight
# mullion - horizontal distance between panels # mullion - horizontal distance between panels
"MullionThickness": self.convert_si_to_unit(0.050), "MullionThickness": usecase.convert_si_to_unit(0.050),
# distance from the first lining to the mullion center # distance from the first lining to the mullion center
"FirstMullionOffset": self.convert_si_to_unit(0.3), "FirstMullionOffset": usecase.convert_si_to_unit(0.3),
# applies to TriplePanelVertical # applies to TriplePanelVertical
# distance from the first lining to the second mullion center # distance from the first lining to the second mullion center
"SecondMullionOffset": self.convert_si_to_unit(0.45), "SecondMullionOffset": usecase.convert_si_to_unit(0.45),
# applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight # TriplePanelLeft, TriplePanelRight
# works similar way to mullion # works similar way to mullion
"TransomThickness": self.convert_si_to_unit(0.050), "TransomThickness": usecase.convert_si_to_unit(0.050),
"FirstTransomOffset": self.convert_si_to_unit(0.3), "FirstTransomOffset": usecase.convert_si_to_unit(0.3),
# applies to TriplePanelHorizontal # applies to TriplePanelHorizontal
"SecondTransomOffset": self.convert_si_to_unit(0.6), "SecondTransomOffset": usecase.convert_si_to_unit(0.6),
"ShapeAspectStyle": None, # DEPRECATED "ShapeAspectStyle": None, # DEPRECATED
}, },
"panel_properties": [ "panel_properties": [
{ {
"FrameDepth": self.convert_si_to_unit(0.035), # by Y "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
"FrameThickness": self.convert_si_to_unit(0.035), # by X "FrameThickness": usecase.convert_si_to_unit(0.035), # by X
# BOTTOM, LEFT, MIDDLE, RIGHT, TOP # BOTTOM, LEFT, MIDDLE, RIGHT, TOP
"PanelPosition": ..., # NEVER USED "PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how window panels operate # defines the basic ways to describe how window panels operate
@@ -283,10 +273,13 @@ class Usecase:
} }
) )
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]] usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]]
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
builder = ShapeBuilder(self.file) builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"] overall_height = self.settings["overall_height"]
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def assign_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = {"product": None, "representation": None} usecase.settings = {"product": None, "representation": None}
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.settings["product"].is_a("IfcProduct"): if self.settings["product"].is_a("IfcProduct"):
product_type = ifcopenshell.util.element.get_type(self.settings["product"]) product_type = ifcopenshell.util.element.get_type(self.settings["product"])
@@ -21,44 +21,41 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def connect_element(file, **usecase_settings) -> None:
def __init__(self, file, **settings): settings = {
self.file = file
self.settings = {
"relating_element": None, "relating_element": None,
"related_element": None, "related_element": None,
"description": None, "description": None,
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value settings[key] = value
def execute(self):
incompatible_connections = [] incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedFrom: for rel in settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo: for rel in settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
if incompatible_connections: if incompatible_connections:
for connection in set(incompatible_connections): for connection in set(incompatible_connections):
history = connection.OwnerHistory history = connection.OwnerHistory
self.file.remove(connection) file.remove(connection)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
for rel in self.settings["relating_element"].ConnectedTo: for rel in settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
rel.Description = self.settings["description"] rel.Description = settings["description"]
return rel return rel
return self.file.createIfcRelConnectsElements( return file.createIfcRelConnectsElements(
ifcopenshell.guid.new(), ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
Description=self.settings["description"], Description=settings["description"],
RelatingElement=self.settings["relating_element"], RelatingElement=settings["relating_element"],
RelatedElement=self.settings["related_element"], RelatedElement=settings["related_element"],
) )
@@ -21,76 +21,73 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def connect_path(file, **usecase_settings) -> None:
def __init__(self, file, **settings): settings = {
self.file = file
self.settings = {
"relating_element": None, "relating_element": None,
"related_element": None, "related_element": None,
"relating_connection": "NOTDEFINED", "relating_connection": "NOTDEFINED",
"related_connection": "NOTDEFINED", "related_connection": "NOTDEFINED",
"description": None, "description": None,
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value settings[key] = value
def execute(self):
incompatible_connections = [] incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedTo: for rel in settings["relating_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"): if not rel.is_a("IfcRelConnectsPathElements"):
continue continue
if rel.RelatedElement == self.settings["related_element"]: if rel.RelatedElement == settings["related_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
elif ( elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"] rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == self.settings["relating_connection"] and rel.RelatingConnectionType == settings["relating_connection"]
): ):
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["relating_element"].ConnectedFrom: for rel in settings["relating_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"): if not rel.is_a("IfcRelConnectsPathElements"):
continue continue
if ( if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"] rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == self.settings["relating_connection"] and rel.RelatedConnectionType == settings["relating_connection"]
): ):
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedFrom: for rel in settings["related_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"): if not rel.is_a("IfcRelConnectsPathElements"):
continue continue
if ( if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"] rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == self.settings["related_connection"] and rel.RelatedConnectionType == settings["related_connection"]
): ):
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo: for rel in settings["related_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"): if not rel.is_a("IfcRelConnectsPathElements"):
continue continue
if rel.RelatedElement == self.settings["relating_element"]: if rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
elif ( elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"] rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == self.settings["related_connection"] and rel.RelatingConnectionType == settings["related_connection"]
): ):
incompatible_connections.append(rel) incompatible_connections.append(rel)
if incompatible_connections: if incompatible_connections:
for connection in set(incompatible_connections): for connection in set(incompatible_connections):
history = connection.OwnerHistory history = connection.OwnerHistory
self.file.remove(connection) file.remove(connection)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
return self.file.createIfcRelConnectsPathElements( return file.createIfcRelConnectsPathElements(
ifcopenshell.guid.new(), ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
Description=self.settings["description"], Description=settings["description"],
RelatingElement=self.settings["relating_element"], RelatingElement=settings["relating_element"],
RelatedElement=self.settings["related_element"], RelatedElement=settings["related_element"],
RelatingConnectionType=self.settings["relating_connection"], RelatingConnectionType=settings["relating_connection"],
RelatedConnectionType=self.settings["related_connection"], RelatedConnectionType=settings["related_connection"],
RelatingPriorities=[], RelatingPriorities=[],
RelatedPriorities=[], RelatedPriorities=[],
) )
@@ -21,10 +21,12 @@ import ifcopenshell.api
import ifcopenshell.util.unit import ifcopenshell.util.unit
class Usecase: def create_2pt_wall(
def __init__(self, file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True): file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True
self.file = file ) -> None:
self.settings = { usecase = Usecase()
usecase.file = file
usecase.settings = {
"element": element, "element": element,
"context": context, "context": context,
"p1": p1, "p1": p1,
@@ -32,9 +34,12 @@ class Usecase:
"elevation": elevation, "elevation": elevation,
"height": height, "height": height,
"thickness": thickness, "thickness": thickness,
"is_si": is_si "is_si": is_si,
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -44,9 +49,9 @@ class Usecase:
length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"])) length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"]))
if not self.settings["is_si"]: if not self.settings["is_si"]:
length=self.convert_unit_to_si(length) length = self.convert_unit_to_si(length)
self.settings["height"]=self.convert_unit_to_si(self.settings["height"]) self.settings["height"] = self.convert_unit_to_si(self.settings["height"])
self.settings["thickness"]=self.convert_unit_to_si(self.settings["thickness"]) self.settings["thickness"] = self.convert_unit_to_si(self.settings["thickness"])
self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0]) self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0])
self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1]) self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1])
self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"]) self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"])
@@ -20,38 +20,35 @@ import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def disconnect_element(file, **usecase_settings) -> None:
def __init__(self, file, **settings): settings = {
self.file = file
self.settings = {
"relating_element": None, "relating_element": None,
"related_element": None, "related_element": None,
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value settings[key] = value
def execute(self):
incompatible_connections = [] incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedTo: for rel in settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["relating_element"].ConnectedFrom: for rel in settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo: for rel in settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedFrom: for rel in settings["related_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["relating_element"]: if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]:
incompatible_connections.append(rel) incompatible_connections.append(rel)
if incompatible_connections: if incompatible_connections:
for connection in set(incompatible_connections): for connection in set(incompatible_connections):
history = connection.OwnerHistory history = connection.OwnerHistory
self.file.remove(connection) file.remove(connection)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -21,38 +21,35 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def disconnect_path(file, **usecase_settings) -> None:
def __init__(self, file, **settings): settings = {
self.file = file
self.settings = {
"relating_element": None, "relating_element": None,
"related_element": None, "related_element": None,
"element": None, "element": None,
"connection_type": None, "connection_type": None,
} }
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value settings[key] = value
def execute(self): if settings["connection_type"] and settings["element"]:
if self.settings["connection_type"] and self.settings["element"]:
connections = [ connections = [
r r
for r in self.settings["element"].ConnectedTo for r in settings["element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == self.settings["connection_type"] if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"]
] + [ ] + [
r r
for r in self.settings["element"].ConnectedFrom for r in settings["element"].ConnectedFrom
if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == self.settings["connection_type"] if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"]
] ]
else: else:
connections = [ connections = [
r r
for r in self.settings["relating_element"].ConnectedTo for r in settings["relating_element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == self.settings["related_element"] if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"]
] ]
for connection in set(connections): for connection in set(connections):
history = connection.OwnerHistory history = connection.OwnerHistory
self.file.remove(connection) file.remove(connection)
if history: if history:
ifcopenshell.util.element.remove_deep2(self.file, history) ifcopenshell.util.element.remove_deep2(file, history)
@@ -27,24 +27,26 @@ from typing import Optional, Union
NPArrayOfFloats = npt.NDArray[np.float64] NPArrayOfFloats = npt.NDArray[np.float64]
class Usecase: def edit_object_placement(
def __init__(
self,
file: ifcopenshell.file, file: ifcopenshell.file,
product: ifcopenshell.entity_instance, product: ifcopenshell.entity_instance,
matrix: Optional[NPArrayOfFloats] = None, matrix: Optional[NPArrayOfFloats] = None,
is_si=True, is_si=True,
should_transform_children=False, should_transform_children=False,
): ) -> ifcopenshell.entity_instance:
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"product": product, "product": product,
"matrix": matrix if matrix is not None else np.eye(4), "matrix": matrix if matrix is not None else np.eye(4),
"is_si": is_si, "is_si": is_si,
"should_transform_children": should_transform_children, "should_transform_children": should_transform_children,
} }
return usecase.execute()
def execute(self) -> ifcopenshell.entity_instance:
class Usecase:
def execute(self):
if not hasattr(self.settings["product"], "ObjectPlacement"): if not hasattr(self.settings["product"], "ObjectPlacement"):
return return
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -17,14 +17,17 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def map_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = {"representation": None} usecase.settings = {"representation": None}
self.ifc_vertices = [] usecase.ifc_vertices = []
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
mapping_source = self.get_mapping_source() mapping_source = self.get_mapping_source()
@@ -19,13 +19,16 @@
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_boolean(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = {"item": None} usecase.settings = {"item": None}
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
item = None item = None
for inverse in self.file.get_inverse(self.settings["item"]): for inverse in self.file.get_inverse(self.settings["item"]):
@@ -19,8 +19,7 @@
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None:
def __init__(self, file: ifcopenshell.file, representation: ifcopenshell.entity_instance):
"""Remove a representation. """Remove a representation.
Also purges representation items and their related elements Also purges representation items and their related elements
@@ -34,22 +33,18 @@ class Usecase:
:return: None :return: None
:rtype: None :rtype: None
""" """
self.file = file settings = {"representation": representation}
self.settings = {"representation": representation}
def execute(self) -> None:
styled_items = set() styled_items = set()
presentation_layer_assignments = set() presentation_layer_assignments = set()
textures = set() textures = set()
colours = set() colours = set()
for subelement in self.file.traverse(self.settings["representation"]): for subelement in file.traverse(settings["representation"]):
if subelement.is_a("IfcRepresentationItem"): if subelement.is_a("IfcRepresentationItem"):
[styled_items.add(s) for s in subelement.StyledByItem or []] [styled_items.add(s) for s in subelement.StyledByItem or []]
# IFC2X3 is using LayerAssignments # IFC2X3 is using LayerAssignments
for s in ( for s in (
subelement.LayerAssignment subelement.LayerAssignment if hasattr(subelement, "LayerAssignment") else subelement.LayerAssignments
if hasattr(subelement, "LayerAssignment")
else subelement.LayerAssignments
): ):
presentation_layer_assignments.add(s) presentation_layer_assignments.add(s)
# IfcTessellatedFaceSet inverses # IfcTessellatedFaceSet inverses
@@ -60,21 +55,21 @@ class Usecase:
presentation_layer_assignments.add(layer) presentation_layer_assignments.add(layer)
ifcopenshell.util.element.remove_deep2( ifcopenshell.util.element.remove_deep2(
self.file, file,
self.settings["representation"], settings["representation"],
also_consider=list(styled_items | presentation_layer_assignments | colours), also_consider=list(styled_items | presentation_layer_assignments | colours),
do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"), do_not_delete=file.by_type("IfcGeometricRepresentationContext"),
) )
for texture in textures: for texture in textures:
ifcopenshell.util.element.remove_deep2(self.file, texture) ifcopenshell.util.element.remove_deep2(file, texture)
for colour in colours: for colour in colours:
ifcopenshell.util.element.remove_deep2(self.file, colour) ifcopenshell.util.element.remove_deep2(file, colour)
to_delete = getattr(self.file, "to_delete", ()) to_delete = getattr(file, "to_delete", ())
for element in styled_items: for element in styled_items:
if not element.Item or element.Item in to_delete: if not element.Item or element.Item in to_delete:
self.file.remove(element) file.remove(element)
for element in presentation_layer_assignments: for element in presentation_layer_assignments:
if all(item in to_delete for item in element.AssignedItems): if all(item in to_delete for item in element.AssignedItems):
self.file.remove(element) file.remove(element)
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def unassign_representation(file, **usecase_settings) -> None:
def __init__(self, file, **settings): usecase = Usecase()
self.file = file usecase.file = file
self.settings = {"product": None, "representation": None} usecase.settings = {"product": None, "representation": None}
for key, value in settings.items(): for key, value in usecase_settings.items():
self.settings[key] = value usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
if self.settings["product"].is_a("IfcProduct"): if self.settings["product"].is_a("IfcProduct"):
self.unassign_product_representation(self.settings["product"], self.settings["representation"]) self.unassign_product_representation(self.settings["product"], self.settings["representation"])
@@ -15,3 +15,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_georeferencing import add_georeferencing
from .edit_georeferencing import edit_georeferencing
from .remove_georeferencing import remove_georeferencing
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def add_georeferencing(file) -> None:
def __init__(self, file):
"""Add empty georeferencing entities to a model """Add empty georeferencing entities to a model
By default, models are not georeferenced. Georeferencing requires two By default, models are not georeferenced. Georeferencing requires two
@@ -41,18 +40,16 @@ class Usecase:
ifcopenshell.api.run("georeference.add_georeferencing", model) ifcopenshell.api.run("georeference.add_georeferencing", model)
""" """
self.file = file
def execute(self):
source_crs = None source_crs = None
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if context.ContextType == "Model": if context.ContextType == "Model":
source_crs = context source_crs = context
break break
if not source_crs: if not source_crs:
return return
projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""}) projected_crs = file.create_entity("IfcProjectedCRS", **{"Name": ""})
self.file.create_entity( file.create_entity(
"IfcMapConversion", "IfcMapConversion",
**{ **{
"SourceCRS": source_crs, "SourceCRS": source_crs,
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_north=None) -> None:
def __init__(self, file, map_conversion=None, projected_crs=None, true_north=None):
"""Edits the attributes of a map conversion, projected CRS, and true north """Edits the attributes of a map conversion, projected CRS, and true north
Setting the correct georeferencing parameters is a complex topic and Setting the correct georeferencing parameters is a complex topic and
@@ -81,13 +80,17 @@ class Usecase:
"Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor! "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor!
}) })
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"map_conversion": map_conversion or {}, "map_conversion": map_conversion or {},
"projected_crs": projected_crs or {}, "projected_crs": projected_crs or {},
"true_north": true_north or [], "true_north": true_north or [],
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
map_conversion = self.file.by_type("IfcMapConversion")[0] map_conversion = self.file.by_type("IfcMapConversion")[0]
projected_crs = self.file.by_type("IfcProjectedCRS")[0] projected_crs = self.file.by_type("IfcProjectedCRS")[0]
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def remove_georeferencing(file) -> None:
def __init__(self, file):
"""Remove georeferencing data """Remove georeferencing data
All georeferencing parameters such as projected CRS and map conversion All georeferencing parameters such as projected CRS and map conversion
@@ -33,13 +32,11 @@ class Usecase:
# Let's change our mind # Let's change our mind
ifcopenshell.api.run("georeference.remove_georeferencing", model) ifcopenshell.api.run("georeference.remove_georeferencing", model)
""" """
self.file = file
def execute(self): map_conversion = file.by_type("IfcMapConversion")[0]
map_conversion = self.file.by_type("IfcMapConversion")[0] projected_crs = file.by_type("IfcProjectedCRS")[0]
projected_crs = self.file.by_type("IfcProjectedCRS")[0] if projected_crs.MapUnit and len(file.get_inverse(projected_crs.MapUnit)) == 1:
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
# TODO: go deeper for conversion units # TODO: go deeper for conversion units
self.file.remove(projected_crs.MapUnit) file.remove(projected_crs.MapUnit)
self.file.remove(projected_crs) file.remove(projected_crs)
self.file.remove(map_conversion) file.remove(map_conversion)
@@ -15,3 +15,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .create_axis_curve import create_axis_curve
from .create_grid_axis import create_grid_axis
from .remove_grid_axis import remove_grid_axis
@@ -22,8 +22,7 @@ import ifcopenshell.util.placement
from mathutils import Matrix # For now, we depend on Blender from mathutils import Matrix # For now, we depend on Blender
class Usecase: def create_axis_curve(file, axis_curve=None, grid_axis=None) -> None:
def __init__(self, file, axis_curve=None, grid_axis=None):
"""Adds curve geometry to a grid axis to represent the axis extents """Adds curve geometry to a grid axis to represent the axis extents
This currently depends on the Blender geometry kernel to function. This currently depends on the Blender geometry kernel to function.
@@ -56,12 +55,16 @@ class Usecase:
ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a) ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a)
ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1) ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1)
""" """
self.file = file usecase = Usecase()
self.settings = { usecase.file = file
usecase.settings = {
"axis_curve": axis_curve, # A Blender object "axis_curve": axis_curve, # A Blender object
"grid_axis": grid_axis, "grid_axis": grid_axis,
} }
return usecase.execute()
class Usecase:
def execute(self): def execute(self):
existing_curve = self.settings["grid_axis"].AxisCurve existing_curve = self.settings["grid_axis"].AxisCurve
if existing_curve and len(self.file.get_inverse(existing_curve)) == 1: if existing_curve and len(self.file.get_inverse(existing_curve)) == 1:
@@ -17,8 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase: def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None) -> None:
def __init__(self, file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None):
"""Adds a new grid axis to a grid """Adds a new grid axis to a grid
An IFC grid will typically have a minimum of two axes which will be An IFC grid will typically have a minimum of two axes which will be
@@ -67,19 +66,17 @@ class Usecase:
axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
axis_tag="1", uvw_axes="VAxes", grid=grid) axis_tag="1", uvw_axes="VAxes", grid=grid)
""" """
self.file = file settings = {
self.settings = {
"axis_tag": axis_tag or "A", "axis_tag": axis_tag or "A",
"same_sense": same_sense or True, "same_sense": same_sense or True,
"uvw_axes": uvw_axes or "UAxes", # Choose which axes "uvw_axes": uvw_axes or "UAxes", # Choose which axes
"grid": grid, "grid": grid,
} }
def execute(self): element = file.create_entity(
element = self.file.create_entity( "IfcGridAxis", **{"AxisTag": settings["axis_tag"], "SameSense": settings["same_sense"]}
"IfcGridAxis", **{"AxisTag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]}
) )
axes = list(getattr(self.settings["grid"], self.settings["uvw_axes"]) or []) axes = list(getattr(settings["grid"], settings["uvw_axes"]) or [])
axes.append(element) axes.append(element)
setattr(self.settings["grid"], self.settings["uvw_axes"], axes) setattr(settings["grid"], settings["uvw_axes"], axes)
return element return element
@@ -19,8 +19,7 @@
import ifcopenshell.util.element import ifcopenshell.util.element
class Usecase: def remove_grid_axis(file, axis=None) -> None:
def __init__(self, file, axis=None):
"""Removes a grid axis from a grid """Removes a grid axis from a grid
:param axis: The IfcGridAxis you want to remove. :param axis: The IfcGridAxis you want to remove.
@@ -44,11 +43,9 @@ class Usecase:
# Let's remove it! # Let's remove it!
ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2) ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
""" """
self.file = file settings = {"axis": axis}
self.settings = {"axis": axis}
def execute(self): if len(file.get_inverse(settings["axis"].AxisCurve)) == 1:
if len(self.file.get_inverse(self.settings["axis"].AxisCurve)) == 1: ifcopenshell.util.element.remove_deep(file, settings["axis"].AxisCurve)
ifcopenshell.util.element.remove_deep(self.file, self.settings["axis"].AxisCurve) file.remove(settings["axis"].AxisCurve)
self.file.remove(self.settings["axis"].AxisCurve) file.remove(settings["axis"])
self.file.remove(self.settings["axis"])
@@ -15,3 +15,10 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_group import add_group
from .assign_group import assign_group
from .edit_group import edit_group
from .remove_group import remove_group
from .unassign_group import unassign_group
from .update_group_products import update_group_products
@@ -20,8 +20,7 @@ import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
class Usecase: def add_group(file, Name="Unnamed", Description=None) -> None:
def __init__(self, file, Name="Unnamed", Description=None):
"""Adds a new group """Adds a new group
An IFC group is an arbitrary collection of products, which are typically An IFC group is an arbitrary collection of products, which are typically
@@ -45,19 +44,17 @@ class Usecase:
ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
""" """
self.file = file settings = {
self.settings = {
"Name": Name or "Unnamed", "Name": Name or "Unnamed",
"Description": Description, "Description": Description,
} }
def execute(self): return file.create_entity(
return self.file.create_entity(
"IfcGroup", "IfcGroup",
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"Name": self.settings["Name"], "Name": settings["Name"],
"Description": self.settings["Description"], "Description": settings["Description"],
} }
) )

Some files were not shown because too many files have changed in this diff Show More