This commit is contained in:
Andrej730
2025-02-20 14:50:55 +05:00
parent b19ece18c6
commit 331f491d50
47 changed files with 577 additions and 330 deletions
@@ -62,42 +62,38 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
# No we don't.
ifcopenshell.api.root.remove_product(model, product=wall)
"""
settings = {"product": product}
representations = []
if settings["product"].is_a("IfcProduct"):
if settings["product"].Representation:
representations = settings["product"].Representation.Representations or []
representations: list[ifcopenshell.entity_instance] = []
if product.is_a("IfcProduct"):
if product.Representation:
representations = product.Representation.Representations or []
else:
representations = []
# remove object placements
object_placement = settings["product"].ObjectPlacement
object_placement = product.ObjectPlacement
if object_placement:
if file.get_total_inverses(object_placement) == 1:
settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work
product.ObjectPlacement = None # remove the inverse for remove_deep2 to work
ifcopenshell.util.element.remove_deep2(file, object_placement)
elif settings["product"].is_a("IfcTypeProduct"):
representations = [rm.MappedRepresentation for rm in settings["product"].RepresentationMaps or []]
elif product.is_a("IfcTypeProduct"):
representations = [rm.MappedRepresentation for rm in product.RepresentationMaps or []]
# remove psets
psets = settings["product"].HasPropertySets or []
psets = product.HasPropertySets or []
for pset in psets:
if file.get_total_inverses(pset) != 1:
continue
ifcopenshell.api.pset.remove_pset(file, product=settings["product"], pset=pset)
ifcopenshell.api.pset.remove_pset(file, product=product, pset=pset)
for representation in representations:
ifcopenshell.api.geometry.unassign_representation(
file, product=settings["product"], representation=representation
)
ifcopenshell.api.geometry.remove_representation(file, **{"representation": representation})
for opening in getattr(settings["product"], "HasOpenings", []) or []:
ifcopenshell.api.geometry.unassign_representation(file, product=product, representation=representation)
ifcopenshell.api.geometry.remove_representation(file, representation=representation)
for opening in getattr(product, "HasOpenings", []) or []:
ifcopenshell.api.feature.remove_feature(file, feature=opening.RelatedOpeningElement)
if settings["product"].is_a("IfcGrid"):
for axis in settings["product"].UAxes + settings["product"].VAxes + (settings["product"].WAxes or ()):
if product.is_a("IfcGrid"):
for axis in product.UAxes + product.VAxes + (product.WAxes or ()):
ifcopenshell.api.grid.remove_grid_axis(file, axis=axis)
def element_exists(element_id):
@@ -108,22 +104,20 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
return False
# TODO: remove object placement and other relationships
for inverse_id in [i.id() for i in file.get_inverse(settings["product"])]:
for inverse_id in [i.id() for i in file.get_inverse(product)]:
try:
inverse = file.by_id(inverse_id)
except:
continue
if inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.pset.remove_pset(
file, product=settings["product"], pset=inverse.RelatingPropertyDefinition
)
ifcopenshell.api.pset.remove_pset(file, product=product, pset=inverse.RelatingPropertyDefinition)
elif inverse.is_a("IfcRelAssociatesMaterial"):
ifcopenshell.api.material.unassign_material(file, products=[settings["product"]])
ifcopenshell.api.material.unassign_material(file, products=[product])
elif inverse.is_a("IfcRelDefinesByType"):
if inverse.RelatingType == settings["product"]:
if inverse.RelatingType == product:
ifcopenshell.api.type.unassign_type(file, related_objects=inverse.RelatedObjects)
else:
ifcopenshell.api.type.unassign_type(file, related_objects=[settings["product"]])
ifcopenshell.api.type.unassign_type(file, related_objects=[product])
elif inverse.is_a("IfcRelSpaceBoundary"):
ifcopenshell.api.boundary.remove_boundary(file, boundary=inverse)
elif inverse.is_a("IfcRelFillsElement"):
@@ -142,7 +136,7 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelNests"):
if inverse.RelatingObject == settings["product"]:
if inverse.RelatingObject == product:
inverse_id = inverse.id()
for subelement in inverse.RelatedObjects:
if subelement.is_a("IfcDistributionPort"):
@@ -153,27 +147,27 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.RelatedObjects == (settings["product"],):
elif inverse.RelatedObjects == (product,):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAggregates"):
if inverse.RelatingObject == settings["product"] or len(inverse.RelatedObjects) == 1:
if inverse.RelatingObject == product or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelContainedInSpatialStructure"):
if inverse.RelatingStructure == settings["product"] or len(inverse.RelatedElements) == 1:
if inverse.RelatingStructure == product or len(inverse.RelatedElements) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelConnectsElements"):
if inverse.is_a("IfcRelConnectsWithRealizingElements"):
if settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any(
el for el in inverse.RealizingElements if el != settings["product"]
if product not in (inverse.RelatingElement, inverse.RelatedElement) and any(
el for el in inverse.RealizingElements if el != product
):
continue
history = inverse.OwnerHistory
@@ -181,15 +175,15 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelConnectsPortToElement"):
if inverse.RelatedElement == settings["product"]:
if inverse.RelatedElement == product:
ifcopenshell.api.root.remove_product(file, product=inverse.RelatingPort)
elif inverse.RelatingPort == settings["product"]:
elif inverse.RelatingPort == product:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelConnectsPorts"):
if settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort):
if product not in (inverse.RelatingPort, inverse.RelatedPort):
# if it's not RelatingPort/RelatedPort then it's optional RealizingElement
# so we keep the relationship
continue
@@ -204,7 +198,7 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAssignsToProduct"):
if inverse.RelatingProduct == settings["product"]:
if inverse.RelatingProduct == product:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
@@ -215,17 +209,17 @@ def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instanc
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelFlowControlElements"):
if inverse.RelatingFlowElement == settings["product"]:
if inverse.RelatingFlowElement == product:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.RelatedControlElements == (settings["product"],):
elif inverse.RelatedControlElements == (product,):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = settings["product"].OwnerHistory
file.remove(settings["product"])
history = product.OwnerHistory
file.remove(product)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -81,7 +81,7 @@ class Usecase:
def execute(self, style: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
self.style = style
attribute_types = {}
attribute_types: dict[str, str] = {}
for attribute in style.wrapped_data.declaration().as_entity().all_attributes():
attribute_type = attribute.type_of_attribute()
if attribute_type.as_aggregation_type() is None:
@@ -248,14 +248,10 @@ class entity_instance:
:param f: A callable that takes a single argument and returns a boolean
value. It represents the condition.
:type f: Callable
:param g: A callable that takes a single argument and returns a
transformed value. It represents the transformation.
:type g: Callable
:param value: Any object, the input value to be processed
:type value: Any
:return: Transformed value
:rtype: Any
Example:
@@ -304,8 +300,6 @@ class entity_instance:
"""Return the data type of a positional attribute of the element
:param attr: The index or name of the attribute
:type attr: Union[int, str]
:rtype: string
"""
attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr)
return self.wrapped_data.get_argument_type(attr_idx)
@@ -314,8 +308,6 @@ class entity_instance:
"""Return the name of a positional attribute of the element
:param attr_idx: The index of the attribute
:type attr_idx: int
:rtype: string
"""
return self.wrapped_data.get_argument_name(attr_idx)
@@ -405,9 +397,7 @@ class entity_instance:
returned IFC class name should include schema name
(e.g. "IFC4.IfcWall" if `True` and "IfcWall" if `False`).
If omitted will act as `False`.
:type args: Union[str, bool]
:returns: Either the name of the class, or a boolean if it passes the check
:rtype: Union[str, bool]
Example:
@@ -423,10 +413,7 @@ class entity_instance:
return self.wrapped_data.is_a(*args)
def id(self) -> int:
"""Return the STEP numerical identifier
:rtype: int
"""
"""Return the STEP numerical identifier"""
return self.wrapped_data.id()
def __eq__(self, other: "entity_instance") -> bool:
@@ -359,7 +359,6 @@ class file:
of those methods.
:param type: Case insensitive name of the IFC class
:type type: string
:param args: The positional arguments of the IFC class
:param kwargs: The keyword arguments of the IFC class
:returns: An entity instance
@@ -482,12 +481,10 @@ class file:
"""Return an IFC entity instance filtered by IFC ID.
:param id: STEP numerical identifier
:type id: int
:raises RuntimeError: If `id` is not found.
:returns: An ifcopenshell.entity_instance
:rtype: ifcopenshell.entity_instance
"""
return self[id]
@@ -495,13 +492,10 @@ class file:
"""Return an IFC entity instance filtered by IFC GUID.
:param guid: GlobalId value in 22-character encoded form
:type guid: string
:raises RuntimeError: If `guid` is not found.
:returns: An ifcopenshell.entity_instance
:rtype: ifcopenshell.entity_instance
"""
return self[guid]
@@ -510,9 +504,7 @@ class file:
If the entity already exists, it is not re-added. Existence of entity is checked by it's `.identity()`.
:param inst: The entity instance to add
:type inst: ifcopenshell.entity_instance
:returns: An ifcopenshell.entity_instance
:rtype: ifcopenshell.entity_instance
"""
if self.transaction:
@@ -530,14 +522,11 @@ class file:
If an IFC type class has subclasses, all entities of those subclasses are also returned.
:param type: The case insensitive type of IFC class to return.
:type type: string
:param include_subtypes: Whether or not to return subtypes of the IFC class
:type include_subtypes: bool
:raises RuntimeError: If `type` is not found in IFC schema.
:returns: A list of ifcopenshell.entity_instance objects
:rtype: list[ifcopenshell.entity_instance]
"""
if include_subtypes:
return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
@@ -625,9 +614,7 @@ class file:
significantly faster.
:param inst: The entity instance to get inverse relationships
:type inst: ifcopenshell.entity_instance
:returns: The total number of references
:rtype: int
"""
return self.wrapped_data.get_total_inverses(inst.wrapped_data)
@@ -639,8 +626,6 @@ class file:
the reference to the deleted will be removed from the aggregate.
:param inst: The entity instance to delete
:type inst: ifcopenshell.entity_instance
:rtype: None
"""
if self.transaction:
self.transaction.store_delete(inst)
@@ -676,9 +661,7 @@ class file:
if None. Supported formats : .ifc, .ifcXML, .ifcZIP (equivalent to
format=".ifc" with zipped=True) For zipped .ifcXML use
format=".ifcXML" with zipped=True
:type format: str
:param zipped: zip the file after it is written
:type zipped: bool
Example: