diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 933a885456..7fa76ea0ff 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -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()