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

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