From 1f43ec870993b503dc03a20fa90e1a9064418a1e Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 5 Jun 2023 15:11:19 +1000 Subject: [PATCH] Fix #3069. (Again) review all occurrences of users_collection and collection.name --- .../bim/module/drawing/annotation.py | 11 ++-- .../blenderbim/bim/module/drawing/helper.py | 2 +- .../blenderbim/bim/module/drawing/operator.py | 4 +- .../blenderbim/bim/module/drawing/prop.py | 2 +- .../blenderbim/bim/module/model/pie.py | 2 +- .../blenderbim/bim/module/model/product.py | 40 ++++++----- .../blenderbim/bim/module/model/profile.py | 2 +- .../blenderbim/bim/module/model/slab.py | 2 +- .../blenderbim/bim/module/model/space.py | 66 ++++++++----------- .../blenderbim/bim/module/model/wall.py | 5 +- .../blenderbim/bim/module/project/operator.py | 3 +- src/blenderbim/blenderbim/core/drawing.py | 2 +- src/blenderbim/blenderbim/core/tool.py | 2 +- src/blenderbim/blenderbim/tool/collector.py | 7 +- src/blenderbim/blenderbim/tool/drawing.py | 10 +-- 15 files changed, 76 insertions(+), 84 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/annotation.py b/src/blenderbim/blenderbim/bim/module/drawing/annotation.py index 8b6819ea68..1556461d38 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/annotation.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/annotation.py @@ -45,7 +45,7 @@ class Annotator: font.name = "OpenGost Type B TT" obj.data.font = font obj.data.BIMTextProperties.font_size = "2.5" - collection = bpy.context.scene.camera.users_collection[0] + collection = bpy.context.scene.camera.BIMObjectProperties.collection collection.objects.link(obj) Annotator.resize_text(obj) return obj @@ -53,9 +53,10 @@ class Annotator: @staticmethod def resize_text(text_obj): camera = None - for obj in text_obj.users_collection[0].objects: - if isinstance(obj.data, bpy.types.Camera): - camera = obj + group = tool.Drawing.get_drawing_group(tool.Ifc.get_entity(text_obj)) + for element in tool.Drawing.get_drawing_elements(group): + if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": + camera = tool.Ifc.get_object(element) break if not camera: return @@ -112,7 +113,7 @@ class Annotator: co1, _, _, _ = Annotator.get_placeholder_coords(camera) matrix_world = camera.matrix_world.copy() matrix_world.translation = co1 - collection = camera.users_collection[0] + collection = camera.BIMObjectProperties.collection if object_type == "TEXT": obj = bpy.data.objects.new(object_type, None) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/helper.py b/src/blenderbim/blenderbim/bim/module/drawing/helper.py index edc6ce4a60..562be3aaae 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/helper.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/helper.py @@ -269,7 +269,7 @@ def get_active_drawing(scene): props = scene.DocProperties try: camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id)) - return camera.users_collection[0], camera + return camera.BIMObjectProperties.collection, camera except: return None, None diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 1caab1cc64..434c1b99db 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -288,7 +288,7 @@ class CreateDrawing(bpy.types.Operator): bpy.ops.render.render(write_still=True) else: previous_visibility = {} - for obj in self.camera.users_collection[0].objects: + for obj in self.camera.BIMObjectProperties.collection.objects: if bpy.context.view_layer.objects.get(obj.name): previous_visibility[obj.name] = obj.hide_get() obj.hide_set(True) @@ -1350,7 +1350,7 @@ class ResizeText(bpy.types.Operator): # TODO: check undo redo def execute(self, context): - for obj in context.scene.camera.users_collection[0].objects: + for obj in context.scene.camera.BIMObjectProperties.collection.objects: if isinstance(obj.data, bpy.types.TextCurve): annotation.Annotator.resize_text(obj) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py index 99d5cef841..ed9791ddab 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py @@ -231,7 +231,7 @@ def toggleDecorations(self, context): toggle = self.should_draw_decorations if toggle: # TODO: design a proper text variable templating renderer - collection = context.scene.camera.users_collection[0] + collection = context.scene.camera.BIMObjectProperties.collection for obj in collection.objects: element = tool.Ifc.get_entity(obj) if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]): diff --git a/src/blenderbim/blenderbim/bim/module/model/pie.py b/src/blenderbim/blenderbim/bim/module/model/pie.py index 5c15b2c3d9..f29e286af9 100644 --- a/src/blenderbim/blenderbim/bim/module/model/pie.py +++ b/src/blenderbim/blenderbim/bim/module/model/pie.py @@ -68,7 +68,7 @@ class PieUpdateContainer(bpy.types.Operator): if not obj.BIMObjectProperties.ifc_definition_id: continue for collection in obj.users_collection: - spatial_obj = bpy.data.objects.get(collection.name) + spatial_obj = collection.BIMCollectionProperties.obj if spatial_obj and spatial_obj.BIMObjectProperties.ifc_definition_id: blenderbim.core.spatial.assign_container( tool.Ifc, tool.Collector, tool.Spatial, structure_obj=spatial_obj, element_obj=obj diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index fc1e5e2026..f938ea1e49 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -128,20 +128,9 @@ class AddConstrTypeInstance(bpy.types.Operator): obj.location = context.scene.cursor.location - collection = None - if ( - building_obj - and building_element - and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering"] - and instance_class in ["IfcWindow", "IfcDoor"] - ): - # Fills should be a sibling to the building element - collection = building_obj.users_collection[0] - if not collection: - collection = context.view_layer.active_layer_collection.collection - + collection = context.view_layer.active_layer_collection.collection collection.objects.link(obj) - collection_obj = bpy.data.objects.get(collection.name) + collection_obj = collection.BIMCollectionProperties.obj bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class) element = tool.Ifc.get_entity(obj) @@ -150,6 +139,26 @@ class AddConstrTypeInstance(bpy.types.Operator): # Update required as core.type.assign_type may change obj.data context.view_layer.update() + if ( + building_obj + and building_element + and building_element.is_a() in ["IfcWall", "IfcWallStandardCase", "IfcCovering"] + and instance_class in ["IfcWindow", "IfcDoor"] + ): + # Fills should be a sibling to the building element + parent = ifcopenshell.util.element.get_aggregate(building_element) + if parent: + parent_obj = tool.Ifc.get_object(parent) + blenderbim.core.aggregate.assign_object( + tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj + ) + else: + parent = ifcopenshell.util.element.get_container(building_element) + parent_obj = tool.Ifc.get_object(parent) + blenderbim.core.spatial.assign_container( + tool.Ifc, tool.Collector, tool.Spatial, structure_obj=parent_obj, element_obj=obj + ) + # set occurences properties for the types defined with modifiers if instance_class in ["IfcWindow", "IfcDoor"]: pset_name = f"BBIM_{instance_class[3:]}" @@ -462,11 +471,6 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator): obj.matrix_world = newmat - def copy_obj(self, obj): - new = obj.copy() - new.data = wall2.data.copy() - wall1.users_collection[0].objects.link(wall2) - blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=wall2) def generate_box(usecase_path, ifc_file, settings): diff --git a/src/blenderbim/blenderbim/bim/module/model/profile.py b/src/blenderbim/blenderbim/bim/module/model/profile.py index 2cc507d9e2..b3a88a2a30 100644 --- a/src/blenderbim/blenderbim/bim/module/model/profile.py +++ b/src/blenderbim/blenderbim/bim/module/model/profile.py @@ -53,7 +53,7 @@ class DumbProfileGenerator: self.axis_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Axis", "GRAPH_VIEW") props = bpy.context.scene.BIMModelProperties self.collection = bpy.context.view_layer.active_layer_collection.collection - self.collection_obj = bpy.data.objects.get(self.collection.name) + self.collection_obj = self.collection.BIMCollectionProperties.obj self.depth = props.extrusion_depth self.rotation = 0 self.location = Vector((0, 0, 0)) diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index 7c9bbceebc..9a756ee83e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -133,7 +133,7 @@ class DumbSlabGenerator: props = bpy.context.scene.BIMModelProperties self.collection = bpy.context.view_layer.active_layer_collection.collection - self.collection_obj = bpy.data.objects.get(self.collection.name) + self.collection_obj = self.collection.BIMCollectionProperties.obj self.depth = sum(thicknesses) * unit_scale self.width = 3 self.length = 3 diff --git a/src/blenderbim/blenderbim/bim/module/model/space.py b/src/blenderbim/blenderbim/bim/module/model/space.py index db60ea9e6e..d37d8a09db 100644 --- a/src/blenderbim/blenderbim/bim/module/model/space.py +++ b/src/blenderbim/blenderbim/bim/module/model/space.py @@ -38,7 +38,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator): @classmethod def poll(cls, context): collection = context.view_layer.active_layer_collection.collection - collection_obj = bpy.data.objects.get(collection.name) + collection_obj = collection.BIMCollectionProperties.obj return tool.Ifc.get_entity(collection_obj) def _execute(self, context): @@ -60,7 +60,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator): relating_type = None collection = context.view_layer.active_layer_collection.collection - collection_obj = bpy.data.objects.get(collection.name) + collection_obj = collection.BIMCollectionProperties.obj if not collection_obj: bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR") return @@ -203,29 +203,19 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): active_obj = bpy.context.active_object if not active_obj: - self.report({'ERROR'}, "No active object. Please select a wall") + self.report({"ERROR"}, "No active object. Please select a wall") return - element = None element = tool.Ifc.get_entity(active_obj) - if element: - if not element.is_a("IfcWall"): - self.report({'ERROR'}, "The active object is not a wall. Please select a wall.") - return + if element and not element.is_a("IfcWall"): + return self.report({"ERROR"}, "The active object is not a wall. Please select a wall.") - collection = active_obj.users_collection[0] - collection_obj = bpy.data.objects.get(collection.name) - if not collection_obj: - self.report({'ERROR'}, "No collection found. Please insert one.") - return - - spatial_element = tool.Ifc.get_entity(collection_obj) - if not spatial_element: - self.report({'ERROR'}, "The collection hasn't an ifc space entity. Please provide one.") - return + container = ifcopenshell.util.element.get_container(element) + if not container: + self.report({"ERROR"}, "The wall is not contained.") if not bpy.context.selected_objects: - self.report({'ERROR'}, "No selected objects found. Please select walls.") + self.report({"ERROR"}, "No selected objects found. Please select walls.") return x, y, z = active_obj.matrix_world.translation.xyz @@ -237,19 +227,18 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): polys = self.get_polygons(boundary_elements) - converted_tolerance = self.get_converted_tolerance(tolerance = 0.03) + converted_tolerance = self.get_converted_tolerance(tolerance=0.03) - union = shapely.ops.unary_union(polys).buffer(converted_tolerance, cap_style = 2, join_style = 2) + union = shapely.ops.unary_union(polys).buffer(converted_tolerance, cap_style=2, join_style=2) - i=0 - for linear_ring in union.interiors: + for i, linear_ring in enumerate(union.interiors): poly = Polygon(linear_ring) - poly = poly.buffer(converted_tolerance, single_sided=True, cap_style = 2, join_style = 2) + poly = poly.buffer(converted_tolerance, single_sided=True, cap_style=2, join_style=2) bm = self.get_bmesh_from_polygon(poly, mat, h) name = "Space" + str(i) - mesh = bpy.data.meshes.new(name = name) + mesh = bpy.data.meshes.new(name=name) bm.to_mesh(mesh) bm.free() @@ -258,13 +247,12 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): self.set_obj_origin_to_bboxcenter(obj) - collection.objects.link(obj) - + context.view_layer.active_layer_collection.collection.objects.link(obj) bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace") - i+=1 - - return {"FINISHED"} - + container_obj = tool.Ifc.get_object(container) + blenderbim.core.spatial.assign_container( + tool.Ifc, tool.Collector, tool.Spatial, structure_obj=container_obj, element_obj=obj + ) def get_boundary_elements(self, selected_objects): boundary_elements = [] @@ -302,15 +290,15 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): def get_converted_tolerance(self, tolerance): model = tool.Ifc.get() project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT") - prefix=getattr(project_unit, "Prefix", None) + prefix = getattr(project_unit, "Prefix", None) converted_tolerance = ifcopenshell.util.unit.convert( - value = tolerance, - from_prefix = None, - from_unit = "METRE", - to_prefix = prefix, - to_unit = project_unit.Name, - ) + value=tolerance, + from_prefix=None, + from_unit="METRE", + to_prefix=prefix, + to_unit=project_unit.Name, + ) return tolerance def get_bmesh_from_polygon(self, poly, mat, h): @@ -335,7 +323,7 @@ class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator): extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)] bmesh.ops.translate(bm, vec=[0.0, 0.0, h], verts=extruded_verts) - bmesh.ops.recalc_face_normals(bm, faces = bm.faces) + bmesh.ops.recalc_face_normals(bm, faces=bm.faces) return bm diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index 0a43c1d021..334286ca87 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -434,7 +434,7 @@ class DumbWallGenerator: props = bpy.context.scene.BIMModelProperties self.collection = bpy.context.view_layer.active_layer_collection.collection - self.collection_obj = bpy.data.objects.get(self.collection.name) + self.collection_obj = self.collection.BIMCollectionProperties.obj self.width = self.layers["thickness"] self.height = props.extrusion_depth self.length = props.length @@ -934,7 +934,8 @@ class DumbWallJoiner: def duplicate_wall(self, wall1): wall2 = wall1.copy() wall2.data = wall2.data.copy() - wall1.users_collection[0].objects.link(wall2) + for collection in wall1.users_collection: + collection.objects.link(wall2) blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=wall2) return wall2 diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index c138a4ae8a..de60bb9f7a 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -371,7 +371,8 @@ class AppendLibraryElement(bpy.types.Operator): if not type_collection: type_collection = bpy.data.collections.new("Types") for collection in bpy.context.view_layer.layer_collection.children: - if "IfcProject/" in collection.name: + collection_obj = collection.BIMCollectionProperties.obj + if collection_obj and tool.Ifc.get_entity(collection_obj).is_a("IfcProject"): collection.collection.children.link(type_collection) collection.children["Types"].hide_viewport = True break diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index e75989b8e4..bce835dffb 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -318,7 +318,7 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None): ifc.run("attribute.edit_attributes", product=group, attributes={"Name": name}) collection = drawing_tool.get_drawing_collection(drawing) if collection: - drawing_tool.set_drawing_collection_name(group, collection) + drawing_tool.set_drawing_collection_name(drawing, collection) reference = drawing_tool.get_drawing_document(drawing) information = drawing_tool.get_reference_document(reference) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 968646da52..a2c4d4c704 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -311,7 +311,7 @@ class Drawing: def run_drawing_activate_model(cls): pass def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass def select_assigned_product(cls, drawing): pass - def set_drawing_collection_name(cls, group, collection): pass + def set_drawing_collection_name(cls, drawing, collection): pass def set_name(cls, element, name): pass def setup_annotation_object(cls, obj, object_type): pass def setup_shading_styles_path(cls, resource_path): pass diff --git a/src/blenderbim/blenderbim/tool/collector.py b/src/blenderbim/blenderbim/tool/collector.py index ef8ef0556f..ae297f656f 100644 --- a/src/blenderbim/blenderbim/tool/collector.py +++ b/src/blenderbim/blenderbim/tool/collector.py @@ -143,11 +143,8 @@ class Collector(blenderbim.core.tool.Collector): return axes_col[0] return bpy.data.collections.new(axes) - if element.is_a("IfcAnnotation"): - for rel in element.HasAssignments or []: - if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING": - name = "IfcGroup/" + rel.RelatingGroup.Name - return bpy.data.collections.get(name) or bpy.data.collections.new(name) + if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": + return cls._create_own_collection(obj) if element.is_a("IfcStructuralMember"): return bpy.data.collections.get("Members") or bpy.data.collections.new("Members") diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index c237b8baec..a16319e416 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -364,7 +364,7 @@ class Drawing(blenderbim.core.tool.Drawing): def get_drawing_collection(cls, drawing): obj = tool.Ifc.get_object(drawing) if obj: - return obj.users_collection[0] + return obj.BIMObjectProperties.collection @classmethod def get_drawing_group(cls, drawing): @@ -759,8 +759,8 @@ class Drawing(blenderbim.core.tool.Drawing): ) @classmethod - def set_drawing_collection_name(cls, group, collection): - collection.name = f"IfcGroup/{group.Name}" + def set_drawing_collection_name(cls, drawing, collection): + collection.name = tool.Loader.get_name(drawing) @classmethod def set_name(cls, element, name): @@ -1486,8 +1486,8 @@ class Drawing(blenderbim.core.tool.Drawing): project_collection.children["Views"].children[collection.name].hide_viewport = True bpy.data.collections.get(collection.name).hide_render = True - project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False - bpy.data.collections.get(camera.users_collection[0].name).hide_render = False + project_collection.children["Views"].children[camera.BIMObjectProperties.collection.name].hide_viewport = False + camera.BIMObjectProperties.collection.hide_render = False tool.Spatial.set_active_object(camera) # Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude