From 36e476b744a97b742e27dc578c38adf2d6df0d4f Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 13 Jul 2023 17:31:51 +0500 Subject: [PATCH] Cleaning up connections on delete_ifc Noticed an error if you connect wall and slab, then remove the slab and try to regenerate the wall - error occured because connection to that slab wasn't removed. The error was: Error: Python: Traceback (most recent call last): File "\3.6\scripts\addons\blenderbim\bim\module\model\workspace.py", line 381, in invoke return self.execute(context) File "\3.6\scripts\addons\blenderbim\tool\ifc.py", line 136, in execute IfcStore.execute_ifc_operator(self, context) File "\3.6\scripts\addons\blenderbim\bim\ifc.py", line 421, in execute_ifc_operator result = getattr(operator, "_execute")(context) File "\3.6\scripts\addons\blenderbim\bim\module\model\workspace.py", line 373, in _execute getattr(self, f"hotkey_{self.hotkey}")() File "\3.6\scripts\addons\blenderbim\bim\module\model\workspace.py", line 465, in hotkey_S_E bpy.ops.bim.join_wall(join_type="T") File "C:\software\Steam\steamapps\common\Blender\3.6\scripts\modules\bpy\ops.py", line 113, in __call__ ret = _op_call(self.idname_py(), None, kw) RuntimeError: Error: Python: Traceback (most recent call last): File "\3.6\scripts\addons\blenderbim\tool\ifc.py", line 136, in execute IfcStore.execute_ifc_operator(self, context) File "\3.6\scripts\addons\blenderbim\bim\ifc.py", line 421, in execute_ifc_operator result = getattr(operator, "_execute")(context) File "\3.6\scripts\addons\blenderbim\bim\module\model\wall.py", line 76, in _execute joiner.join_E(context.active_object, context.scene.cursor.location) File "\3.6\scripts\addons\blenderbim\bim\module\model\wall.py", line 1038, in join_E self.recreate_wall(element1, wall1, axis, body) File "\3.6\scripts\addons\blenderbim\bim\module\model\wall.py", line 1145, in recreate_wall height = self.clip(obj, tool.Ifc.get_object(rel.RelatingElement)) File "\3.6\scripts\addons\blenderbim\tool\ifc.py", line 89, in get_object return IfcStore.get_element(element.id()) AttributeError: 'NoneType' object has no attribute 'id' --- .../blenderbim/bim/module/model/wall.py | 27 +++++++++---------- .../blenderbim/bim/module/model/workspace.py | 3 ++- src/blenderbim/blenderbim/tool/geometry.py | 6 +++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index aa94348596..7494146aee 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -60,11 +60,11 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator): for obj in selected_objs: joiner.unjoin(obj) return {"FINISHED"} - + if not context.active_object or not context.active_object.BIMObjectProperties.ifc_definition_id: self.report({"ERROR"}, f"No active object selected") return {"CANCELLED"} - + for obj in selected_objs: tool.Geometry.clear_scale(obj) @@ -75,7 +75,7 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator): if len(selected_objs) == 1: joiner.join_E(context.active_object, context.scene.cursor.location) return {"FINISHED"} - + if self.join_type in ("L", "V"): if len(selected_objs) != 2: self.report({"ERROR"}, f"It requires 2 selected objects to do join of type {self.join_type}") @@ -86,7 +86,7 @@ class JoinWall(bpy.types.Operator, tool.Ifc.Operator): elif self.join_type == "V": joiner.join_V(another_selected_object, context.active_object) return {"FINISHED"} - + if self.join_type == "T": elements = [tool.Ifc.get_entity(o) for o in context.selected_objects] layer2_elements = [] @@ -560,7 +560,7 @@ class DumbWallGenerator: rotation = math.atan2(normal[1], normal[0]) rotated_y_axis = Matrix.Rotation(-rotation, 4, "Z")[1].xyz - + # since wall thickness goes by local Y+ axis # we find best position for the next wall # by finding the face of another wall that will be very close to the some test point. @@ -1137,7 +1137,7 @@ class DumbWallJoiner: previous_matrix = obj.matrix_world.copy() previous_origin = previous_matrix.col[3].to_2d() - obj.matrix_world[0][3], obj.matrix_world[1][3] = self.body[0] + obj.matrix_world.col[3].xy = self.body[0] bpy.context.view_layer.update() for rel in element.ConnectedFrom: @@ -1432,6 +1432,7 @@ class DumbWallJoiner: return True def clip(self, wall1, slab2): + """returns height of the clipped wall, adds clipping plane to `clippings`""" element1 = tool.Ifc.get_entity(wall1) element2 = tool.Ifc.get_entity(slab2) @@ -1441,19 +1442,15 @@ class DumbWallJoiner: bases = [axis1["base"][0].to_3d(), axis1["base"][1].to_3d(), axis1["side"][0].to_3d(), axis1["side"][1].to_3d()] extrusion = self.get_extrusion_data(tool.Ifc.get().by_id(wall1.data.BIMMeshProperties.ifc_definition_id)) - d = wall1.matrix_world.to_quaternion() @ extrusion["direction"] + wall_dir = wall1.matrix_world.to_quaternion() @ extrusion["direction"] slab_pt = slab2.matrix_world @ Vector((0, 0, 0)) slab_dir = slab2.matrix_world.to_quaternion() @ Vector((0, 0, -1)) - tops = [mathutils.geometry.intersect_line_plane(b, b + d, slab_pt, slab_dir) for b in bases] - - i_bottom = None - i_top = None - for i, co in enumerate(tops): - if i_top is None or co[2] > i_top[2]: - i_top = co - i_bottom = bases[i] + tops = [mathutils.geometry.intersect_line_plane(b, b + wall_dir, slab_pt, slab_dir) for b in bases] + top_index = max(range(4), key=lambda i: tops[i].z) + i_top = tops[top_index] + i_bottom = bases[top_index] quaternion = slab2.matrix_world.to_quaternion() x_axis = quaternion @ Vector((1, 0, 0)) diff --git a/src/blenderbim/blenderbim/bim/module/model/workspace.py b/src/blenderbim/blenderbim/bim/module/model/workspace.py index 6fefce75b1..2b7bface16 100644 --- a/src/blenderbim/blenderbim/bim/module/model/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/model/workspace.py @@ -457,7 +457,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): if len(bpy.context.selected_objects) == 1: if self.active_material_usage == "LAYER3": - # Edit LAYER2 profile + # Edit LAYER3 profile if bpy.context.active_object and bpy.context.active_object.mode == "OBJECT": bpy.ops.bim.enable_editing_extrusion_profile() elif self.active_material_usage == "LAYER2": @@ -469,6 +469,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): else: # Edit SWEPTSOLID profile (assuming single profile for now) bpy.ops.bim.enable_editing_extrusion_profile() + elif self.active_material_usage == "LAYER2" and selected_usages.get("PROFILE", []): # Extend PROFILEs to LAYER2 [o.select_set(False) for o in selected_usages.get("LAYER3", [])] diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index 5e13ab5dd2..1bd843764b 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -75,6 +75,12 @@ class Geometry(blenderbim.core.tool.Geometry): if element.is_a("IfcRelSpaceBoundary"): ifcopenshell.api.run("boundary.remove_boundary", tool.Ifc.get(), boundary=element) return bpy.data.objects.remove(obj) + + if element.is_a("IfcElement"): + connections = element.ConnectedTo + element.ConnectedFrom + for connection in connections: + tool.Ifc.get().remove(connection) + collection = obj.BIMObjectProperties.collection if collection: parent = ifcopenshell.util.element.get_aggregate(element)