(self)
| 78 | self.is_solid = is_solid |
| 79 | |
| 80 | def patch(self) -> None: |
| 81 | from math import degrees |
| 82 | |
| 83 | import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import] |
| 84 | import bonsai.tool as tool |
| 85 | import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import] |
| 86 | |
| 87 | props = tool.Project.get_project_props() |
| 88 | props.should_use_native_meshes = True |
| 89 | bpy.ops.bim.load_project(filepath=self.filepath) |
| 90 | |
| 91 | old_history_size = tool.Ifc.get().history_size |
| 92 | old_undo_steps = bpy.context.preferences.edit.undo_steps |
| 93 | tool.Ifc.get().history_size = 0 |
| 94 | bpy.context.preferences.edit.undo_steps = 0 |
| 95 | |
| 96 | angle_threshold = 0.3 |
| 97 | |
| 98 | for obj in bpy.data.objects: |
| 99 | ifc_id = tool.Blender.get_ifc_definition_id(obj) |
| 100 | if not ifc_id or not obj.data: |
| 101 | continue |
| 102 | if not obj.data.polygons: |
| 103 | continue |
| 104 | data = obj.data |
| 105 | bm = bmesh.new() |
| 106 | bm.from_mesh(data) |
| 107 | |
| 108 | bm.faces.ensure_lookup_table() |
| 109 | faces_to_delete = [] |
| 110 | |
| 111 | if self.is_solid: |
| 112 | for face in bm.faces: |
| 113 | if face.normal.z < 0.5: |
| 114 | faces_to_delete.append(face) |
| 115 | |
| 116 | bmesh.ops.delete(bm, geom=faces_to_delete, context="FACES_ONLY") |
| 117 | |
| 118 | bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.01) |
| 119 | bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method="BEAUTY", ngon_method="BEAUTY") |
| 120 | bm.faces.ensure_lookup_table() |
| 121 | for polygon in bm.faces: |
| 122 | try: |
| 123 | v1, v2, v3 = [v.co.to_2d() for v in polygon.verts] |
| 124 | d1 = degrees((v2 - v1).angle(v3 - v1)) |
| 125 | d2 = degrees((v3 - v2).angle(v1 - v2)) |
| 126 | d3 = degrees((v1 - v3).angle(v2 - v3)) |
| 127 | if d1 < angle_threshold or d2 < angle_threshold or d3 < angle_threshold: |
| 128 | bm.faces.remove(polygon) |
| 129 | except: |
| 130 | bm.faces.remove(polygon) |
| 131 | if bm.faces: |
| 132 | if not self.is_solid: |
| 133 | bmesh.ops.recalc_face_normals(bm, faces=bm.faces) |
| 134 | for face in bm.faces: |
| 135 | global_normal = obj.matrix_world.to_3x3() @ face.normal |
| 136 | if global_normal.z < 0: |
| 137 | face.normal_flip() |
nothing calls this directly
no test coverage detected