mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Tools for handling shared psets #5291
In IFC it's possible for a property set to be assigned to multiple elements and which may lead to confusing behaviour when you edit a pset on one element and other element seems to get edited too. Which makes it worse is that that it is possible that some software is might be doing this unintentionally when exporting IFC (as some sort of optimization as storing 1 is more optimal than n copies of it). So now there are some tools in Bonsai and in IfcOpenShell to handle the shared psest: 1) Indication that property is shared - https://imgur.com/a/9dd3jST (similar to how Blender indicates ID data-block users). You can click on it to "unshare" the pset - a new copy for the pset will be created and it's going to be linked only to the active object. 2) api pset.unshare_pset method that does the same. And util.element.get_elements_using_pset method that encapsulates schema differences and different approaches for occurrences/types. 3) ifcpatch recipe 'UnsharePsets' that's making all property sets in the IFC file to have just 1 element that's using them. You can limit the affected elements by providing query. ifcpatch recipe is also available in Bonsai - https://i.imgur.com/aOCx7HI.png
This commit is contained in:
@@ -29,6 +29,7 @@ from .add_qto import add_qto
|
||||
from .edit_pset import edit_pset
|
||||
from .edit_qto import edit_qto
|
||||
from .remove_pset import remove_pset
|
||||
from .unshare_pset import unshare_pset
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
@@ -38,4 +39,5 @@ __all__ = [
|
||||
"edit_pset",
|
||||
"edit_qto",
|
||||
"remove_pset",
|
||||
"unshare_pset",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def unshare_pset(
|
||||
file: ifcopenshell.file,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
pset: ifcopenshell.entity_instance,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""Copy a shared pset as linked only to the provided elements.
|
||||
|
||||
Note that method will create a copy of the pset for each element provided.
|
||||
|
||||
:param products: Elements (or element types) to link the pset to.
|
||||
:param pset: Shared property set.
|
||||
:return: List of copied property sets.
|
||||
"""
|
||||
is_ifc2x3 = file.schema == "IFC2X3"
|
||||
products_occurrences: set[ifcopenshell.entity_instance] = set()
|
||||
products_types: set[ifcopenshell.entity_instance] = set()
|
||||
for product in products:
|
||||
if product.is_a("IfcTypeProduct"):
|
||||
products_types.add(product)
|
||||
else:
|
||||
products_occurrences.add(product)
|
||||
|
||||
# Check occurrences using pset.
|
||||
rels = pset.PropertyDefinitionOf if is_ifc2x3 else pset.DefinesOccurrence
|
||||
for rel in rels:
|
||||
objs = set(rel.RelatedObjects)
|
||||
if not any(p in objs for p in products_occurrences):
|
||||
continue
|
||||
objs.difference_update(products_occurrences)
|
||||
if objs:
|
||||
rel.RelatedObjects = list(objs)
|
||||
else:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
# Check types using pset.
|
||||
for product in products_types:
|
||||
if not (psets := product.HasPropertySets):
|
||||
continue
|
||||
psets: list[ifcopenshell.entity_instance] = list(psets)
|
||||
psets.remove(pset)
|
||||
product.HasPropertySets = psets
|
||||
|
||||
def assign_pset(product: ifcopenshell.entity_instance, pset: ifcopenshell.entity_instance) -> None:
|
||||
if product.is_a("IfcTypeProduct"):
|
||||
psets = list(product.HasPropertySets or [])
|
||||
if not psets:
|
||||
psets = []
|
||||
product.HasPropertySets = psets + [pset]
|
||||
return
|
||||
|
||||
file.create_entity(
|
||||
"IfcRelDefinesByProperties",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [product],
|
||||
"RelatingPropertyDefinition": pset_copy,
|
||||
},
|
||||
)
|
||||
|
||||
pset_copies: list[ifcopenshell.entity_instance] = []
|
||||
for product in products:
|
||||
pset_copy = ifcopenshell.util.element.copy_deep(file, pset)
|
||||
pset_copies.append(pset_copy)
|
||||
assign_pset(product, pset_copy)
|
||||
|
||||
return pset_copies
|
||||
@@ -422,6 +422,18 @@ def get_properties(
|
||||
return results
|
||||
|
||||
|
||||
def get_elements_using_pset(pset: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
"""Retrieve the elements (or element types) that are using the provided property set."""
|
||||
is_ifc2x3 = pset.file.schema == "IFC2X3"
|
||||
elements = set()
|
||||
rels = pset.PropertyDefinitionOf if is_ifc2x3 else pset.DefinesOccurrence
|
||||
for rel in rels:
|
||||
elements.update(rel.RelatedObjects)
|
||||
for element_type in pset.DefinesType:
|
||||
elements.add(element_type)
|
||||
return elements
|
||||
|
||||
|
||||
def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
|
||||
"""Retrieves the PrefefinedType attribute of an element.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user