mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-07 08:21:42 +00:00
Add Name to Linked Aggregates and keep product assignment after refreshing.
See #6831. Warning: this is a breaking change. Linked Aggregate names are numbered to keep them consistent when reopening the file instead of relying on Blender auto numbering objects with the same name. However, for annotation purposes, the numbering is irrelevant, and the user might want to use just the aggregate name. So it was added a "Name" to the "BBIM_Linked_Aggregate" Pset that can be used in these cases. When refreshing the linked aggregates, their names will be updated to reflect the "Name" value and the product assignment will be preserved to keep annotations in sync. In order to adapt existing files to this new Pset structure see this gist: https://gist.github.com/brunoperdigao/37a5b838935b15a91aa70b2bc4de325f
This commit is contained in:
@@ -1192,16 +1192,20 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
obj = tool.Ifc.get_object(part)
|
||||
obj.select_set(True)
|
||||
|
||||
def add_linked_aggregate_pset(part, index):
|
||||
pset = ifcopenshell.util.element.get_pset(part, self.pset_name)
|
||||
def add_linked_aggregate_pset(element, index):
|
||||
pset = ifcopenshell.util.element.get_pset(element, self.pset_name)
|
||||
if index == 0:
|
||||
properties = {"Index": index, "Name": element.Name, "Aggregate_Index": 0}
|
||||
else:
|
||||
properties = {"Index": index}
|
||||
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=part, name=self.pset_name)
|
||||
pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name=self.pset_name)
|
||||
|
||||
ifcopenshell.api.pset.edit_pset(
|
||||
ifc_file,
|
||||
pset=pset,
|
||||
properties={"Index": index},
|
||||
properties=properties,
|
||||
)
|
||||
|
||||
index += 1
|
||||
@@ -1233,18 +1237,9 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
|
||||
)
|
||||
|
||||
number = len(group_elements) - 1
|
||||
number = f"{number:02d}"
|
||||
pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Linked_Aggregate")
|
||||
new_obj = tool.Ifc.get_object(new[0])
|
||||
pattern1 = r"_\d"
|
||||
if re.findall(pattern1, new_obj.name):
|
||||
split_name = new_obj.name.split("_")
|
||||
new_obj.name = split_name[0] + "_" + number
|
||||
continue
|
||||
pattern2 = r"\.\d{3}"
|
||||
if re.findall(pattern2, new_obj.name):
|
||||
split_name = new_obj.name.split(".")
|
||||
new_obj.name = split_name[0] + "_" + number
|
||||
new_obj.name = pset["Name"] + "_" + str(pset["Aggregate_Index"])
|
||||
|
||||
def get_max_index(parts):
|
||||
psets = [ifcopenshell.util.element.get_pset(p, "BBIM_Linked_Aggregate") for p in parts]
|
||||
@@ -1257,16 +1252,6 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
|
||||
def copy_linked_aggregate_data(old_to_new):
|
||||
for old, new in old_to_new.items():
|
||||
pset = ifcopenshell.util.element.get_pset(old, "BBIM_Linked_Aggregate")
|
||||
if pset:
|
||||
new_pset = ifcopenshell.api.pset.add_pset(ifc_file, product=new[0], name=self.pset_name)
|
||||
|
||||
ifcopenshell.api.pset.edit_pset(
|
||||
ifc_file,
|
||||
pset=new_pset,
|
||||
properties={"Index": pset["Index"]},
|
||||
)
|
||||
|
||||
if new[0].is_a("IfcElementAssembly"):
|
||||
linked_aggregate_group = [
|
||||
r.RelatingGroup
|
||||
@@ -1276,6 +1261,28 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
|
||||
]
|
||||
ifcopenshell.api.group.assign_group(ifc_file, group=linked_aggregate_group[0], products=new)
|
||||
|
||||
group_elements: list[ifcopenshell.entity_instance] = next(
|
||||
r.RelatedObjects
|
||||
for r in getattr(new[0], "HasAssignments", []) or []
|
||||
if r.is_a("IfcRelAssignsToGroup")
|
||||
if "BBIM_Linked_Aggregate" in r.RelatingGroup.Name
|
||||
)
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(old, "BBIM_Linked_Aggregate")
|
||||
if pset:
|
||||
new_pset = ifcopenshell.api.pset.add_pset(ifc_file, product=new[0], name=self.pset_name)
|
||||
if pset["Index"] == 0:
|
||||
properties = {"Index": pset["Index"], "Name": pset["Name"], "Aggregate_Index": len(group_elements) - 1}
|
||||
else:
|
||||
properties = {"Index": pset["Index"]}
|
||||
|
||||
ifcopenshell.api.pset.edit_pset(
|
||||
ifc_file,
|
||||
pset=new_pset,
|
||||
properties=properties,
|
||||
)
|
||||
|
||||
|
||||
def get_location_from_3d_cursor(old_to_new, aggregate):
|
||||
base_obj = tool.Ifc.get_object(aggregate)
|
||||
base_obj_location = base_obj.location.copy()
|
||||
@@ -1357,7 +1364,7 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.pset_name = "BBIM_Linked_Aggregate"
|
||||
refresh_start_time = time()
|
||||
old_to_new = {}
|
||||
original_names: dict[int, dict[int, str]] = {}
|
||||
original_data: dict[int, dict[int, str]] = {}
|
||||
|
||||
def delete_objects(element: ifcopenshell.entity_instance) -> None:
|
||||
"""Remove IfcElementAssembly and it's parts."""
|
||||
@@ -1372,25 +1379,51 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Geometry.delete_ifc_object(tool.Ifc.get_object(element))
|
||||
|
||||
def get_original_names(element: ifcopenshell.entity_instance) -> dict[int, dict[int, str]]:
|
||||
def get_assignments(element: ifcopenshell.entity_instance) -> list:
|
||||
annotations = []
|
||||
inverse = list(tool.Ifc.get().get_inverse(element))
|
||||
assignments = [a for a in inverse if a.is_a("IfcRelAssignsToProduct")]
|
||||
if not assignments:
|
||||
pass
|
||||
else:
|
||||
for assignment in assignments:
|
||||
annotations = assignment.RelatedObjects
|
||||
return annotations
|
||||
|
||||
def assign_to_annotations(obj, assignments):
|
||||
for assignment in assignments:
|
||||
product = tool.Ifc.get_entity(obj)
|
||||
annotation = tool.Ifc.get_object(assignment)
|
||||
if annotation:
|
||||
bonsai.core.drawing.edit_assigned_product(tool.Ifc, tool.Drawing, obj=annotation, product=product)
|
||||
else:
|
||||
existing_product = tool.Drawing.get_assigned_product(assignment)
|
||||
if existing_product != product:
|
||||
if existing_product:
|
||||
tool.Ifc.run("drawing.unassign_product", relating_product=existing_product, related_object=assignment)
|
||||
if product:
|
||||
tool.Ifc.run("drawing.assign_product", relating_product=product, related_object=assignment)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
def get_original_data(element: ifcopenshell.entity_instance) -> dict[int, dict[int, str]]:
|
||||
group = next(
|
||||
r.RelatingGroup
|
||||
for r in getattr(element, "HasAssignments", []) or []
|
||||
if r.is_a("IfcRelAssignsToGroup")
|
||||
if self.group_name in r.RelatingGroup.Name
|
||||
).id()
|
||||
original_names[group] = {}
|
||||
original_data[group] = {}
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element, self.pset_name)
|
||||
index = pset["Index"]
|
||||
original_names[group][index] = tool.Ifc.get_object(element).name
|
||||
annotations = get_assignments(element)
|
||||
original_data[group][index] = {"Name": str(pset["Aggregate_Index"]), "Assignment": annotations} # Get the link index number. The name will come from annotation tag
|
||||
|
||||
parts = ifcopenshell.util.element.get_parts(element)
|
||||
if parts:
|
||||
for part in parts:
|
||||
if part.is_a("IfcElementAssembly"):
|
||||
# TODO: missing assignment.
|
||||
original_names | get_original_names(part)
|
||||
original_data | get_original_data(part)
|
||||
else:
|
||||
try:
|
||||
pset = ifcopenshell.util.element.get_pset(part, self.pset_name)
|
||||
@@ -1398,11 +1431,12 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
if pset:
|
||||
index = pset["Index"]
|
||||
original_names[group][index] = tool.Ifc.get_object(part).name
|
||||
annotations = get_assignments(part)
|
||||
original_data[group][index] = {"Name": tool.Ifc.get_object(part).name, "Assignment": annotations}
|
||||
|
||||
return original_names
|
||||
return original_data
|
||||
|
||||
def set_original_name(obj: bpy.types.Object, original_names: dict[int, dict[int, str]]) -> None:
|
||||
def set_original_data(obj: bpy.types.Object, original_data: dict[int, dict[int, str]]) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
if ifcopenshell.util.element.get_parts(
|
||||
@@ -1410,24 +1444,40 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
): # if element has parts it means it is the base of and aggregate or sub-aggregate
|
||||
aggregate = element
|
||||
|
||||
group = [
|
||||
group = next(
|
||||
r.RelatingGroup
|
||||
for r in getattr(aggregate, "HasAssignments", []) or []
|
||||
if r.is_a("IfcRelAssignsToGroup")
|
||||
if self.group_name in r.RelatingGroup.Name
|
||||
]
|
||||
).id()
|
||||
if not group:
|
||||
return
|
||||
|
||||
group = group[0].id()
|
||||
|
||||
pset = ifcopenshell.util.element.get_pset(element, self.pset_name)
|
||||
index = pset["Index"]
|
||||
|
||||
try:
|
||||
obj.name = original_names[group][index]
|
||||
except:
|
||||
return
|
||||
if index == 0:
|
||||
obj.name = pset["Name"]
|
||||
ifc_file = tool.Ifc.get()
|
||||
ifcopenshell.api.pset.edit_pset(
|
||||
ifc_file,
|
||||
ifc_file.by_id(pset["id"]),
|
||||
properties={"Aggregate_Index": int(original_data[group][index]["Name"])},
|
||||
)
|
||||
assignments = original_data[group][index]["Assignment"]
|
||||
if assignments:
|
||||
assign_to_annotations(obj, assignments)
|
||||
else:
|
||||
try:
|
||||
obj.name = original_data[group][index]["Name"]
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
assignments = original_data[group][index]["Assignment"]
|
||||
except:
|
||||
assignments = []
|
||||
if assignments:
|
||||
assign_to_annotations(obj, assignments)
|
||||
|
||||
def get_element_assembly(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if element.is_a("IfcElementAssembly"):
|
||||
@@ -1525,6 +1575,9 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
base_instance = active_element
|
||||
instances_to_refresh = elements
|
||||
|
||||
base_pset = ifcopenshell.util.element.get_pset(base_instance, self.pset_name)
|
||||
base_obj = tool.Ifc.get_object(base_instance)
|
||||
base_obj.name = base_pset["Name"] + "_" + str(base_pset["Aggregate_Index"])
|
||||
for element in instances_to_refresh:
|
||||
if element.GlobalId == base_instance.GlobalId:
|
||||
continue
|
||||
@@ -1533,7 +1586,7 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
selected_matrix, duplicate_matrix = get_original_matrix(element, base_instance)
|
||||
|
||||
original_names = get_original_names(element)
|
||||
original_data = get_original_data(element)
|
||||
|
||||
delete_objects(element)
|
||||
|
||||
@@ -1561,7 +1614,7 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
for old, new in old_to_new.items():
|
||||
new_obj = tool.Ifc.get_object(new[0])
|
||||
set_original_name(new_obj, original_names)
|
||||
set_original_data(new_obj, original_data)
|
||||
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user