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:
Andrej730
2024-09-04 16:54:24 +05:00
parent 3c3b56d1ae
commit 691815fd41
10 changed files with 409 additions and 2 deletions
@@ -29,6 +29,7 @@ classes = (
operator.EnablePsetEditing,
operator.RemovePset,
operator.TogglePsetExpansion,
operator.UnsharePset,
operator.BIM_OT_add_property_to_edit,
operator.BIM_OT_remove_property_to_edit,
operator.BIM_OT_clear_list,
+5 -1
View File
@@ -46,18 +46,22 @@ def refresh():
class Data:
@classmethod
def psetqtos(cls, element, psets_only=False, qtos_only=False):
def psetqtos(cls, element: ifcopenshell.entity_instance, psets_only: bool = False, qtos_only: bool = False):
ifc_file = tool.Ifc.get()
results = []
psetqtos = ifcopenshell.util.element.get_psets(
element, psets_only=psets_only, qtos_only=qtos_only, should_inherit=False
)
for name, data in sorted(psetqtos.items()):
pset = ifc_file.by_id(data["id"])
pset_uses = ifcopenshell.util.element.get_elements_using_pset(pset)
results.append(
{
"id": data["id"],
"Name": name,
"is_expanded": is_expanded.get(data["id"], True),
"Properties": [{"Name": k, "NominalValue": v} for k, v in sorted(data.items()) if k != "id"],
"shared_pset_uses": len(pset_uses),
}
)
return sorted(results, key=lambda v: v["Name"])
@@ -19,6 +19,7 @@
import bpy
import json
import ifcopenshell.api
import ifcopenshell.api.pset
import ifcopenshell.util.attribute
import ifcopenshell.util.element
import ifcopenshell.util.pset
@@ -190,6 +191,36 @@ class AddPset(bpy.types.Operator, tool.Ifc.Operator):
core.add_pset(tool.Ifc, tool.Pset, tool.Blender, obj_name=self.obj, obj_type=self.obj_type)
class UnsharePset(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unshare_pset"
bl_label = "Unshare Pset"
bl_description = (
"Click to copy a pset as linked only to the active object.\n"
"Otherwise changing a pset shared by multiple elements "
"will change it's properties for all the elements it's linked to, not just for the active object"
)
bl_options = {"REGISTER", "UNDO"}
description_: bpy.props.StringProperty(name="Custom Tooltip Description")
pset_id: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()
@classmethod
def description(cls, context, properties):
if not properties.description_:
return cls.bl_description
return f"{properties.description_}{cls.bl_description}"
def _execute(self, context):
# TODO: move to core
ifc_file = tool.Ifc.get()
pset = ifc_file.by_id(self.pset_id)
element_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type)
assert element_id
element = ifc_file.by_id(element_id)
ifcopenshell.api.pset.unshare_pset(ifc_file, [element], pset)
class AddQto(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_qto"
bl_label = "Add Qto"
+11 -1
View File
@@ -120,7 +120,17 @@ def draw_psetqto_ui(
op.obj = obj_name
op.obj_type = obj_type
elif not props.active_pset_id:
row.label(text=pset["Name"], icon="COPY_ID")
row.label(text=f'{pset["Name"]}', icon="COPY_ID")
if (shared := pset["shared_pset_uses"]) > 1:
unshare_pset_row = row.row(align=True)
unshare_pset_row.alignment = "RIGHT"
op = unshare_pset_row.operator("bim.unshare_pset", text=str(shared))
op.description_ = f"Pset is reused by {shared} elements.\n\n"
op.pset_id = pset_id
op.obj = obj_name
op.obj_type = obj_type
op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="")
op.pset_id = pset_id
op.obj = obj_name