The experimental cutting tool now supports a bisect mode for batch cuts

This commit is contained in:
Dion Moult
2025-03-21 16:46:42 +11:00
parent 3231752fef
commit ea84f829af
3 changed files with 91 additions and 5 deletions
+16 -2
View File
@@ -125,15 +125,25 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
"Will unassign element from a type if type has a representation."
)
bl_options = {"REGISTER", "UNDO"}
mode: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return context.selected_objects and tool.Ifc.get()
return context.selected_objects
def _execute(self, context):
cutter = context.active_object
objs = [o for o in context.selected_objects if o != cutter]
if not tool.Ifc.get():
if self.mode == "BOOLEAN":
tool.Misc.boolean_objects_with_cutter(objs, cutter)
elif self.mode == "BISECT":
tool.Misc.bisect_objects_with_cutter(objs, cutter)
for obj in objs:
bpy.data.objects.remove(obj)
return
objs_to_cut = []
# Splitting only works on meshes
for obj in objs:
@@ -169,7 +179,11 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
objs_to_cut.append(obj)
new_objs = tool.Misc.split_objects_with_cutter(objs_to_cut, cutter)
if self.mode == "BOOLEAN":
new_objs = tool.Misc.boolean_objects_with_cutter(objs_to_cut, cutter)
elif self.mode == "BISECT":
new_objs = tool.Misc.bisect_objects_with_cutter(objs_to_cut, cutter)
for obj in new_objs:
bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
bpy.ops.bim.update_representation(obj=obj.name)
+3 -2
View File
@@ -39,8 +39,9 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
row = layout.split(factor=0.2, align=True)
row.prop(props, "total_storeys", text="")
row.operator("bim.resize_to_storey").total_storeys = props.total_storeys
row = layout.row()
row.operator("bim.split_along_edge")
row = layout.row(align=True)
row.operator("bim.split_along_edge", text="Split Along Edge").mode = "BOOLEAN"
row.operator("bim.split_along_edge", text="Bisect At Faces").mode = "BISECT"
row = layout.row()
row.operator("bim.get_connected_system_elements")
row = layout.row()
+72 -1
View File
@@ -99,7 +99,7 @@ class Misc(bonsai.core.tool.Misc):
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
@classmethod
def split_objects_with_cutter(
def boolean_objects_with_cutter(
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
) -> list[bpy.types.Object]:
cutter_mesh = cutter.data
@@ -144,3 +144,74 @@ class Misc(bonsai.core.tool.Misc):
bm.free()
bm_flipped.free()
return new_objs
@classmethod
def bisect_objects_with_cutter(
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
) -> list[bpy.types.Object]:
cutter_mesh = cutter.data
assert isinstance(cutter_mesh, bpy.types.Mesh)
bm = bmesh.new()
bm.from_mesh(cutter_mesh)
bm.faces.ensure_lookup_table()
planes = []
for face in bm.faces:
no = face.normal.to_4d()
no.w = 0.0
no = cutter.matrix_world @ no
co = cutter.matrix_world @ face.verts[0].co
planes.append((co, no))
bm.free()
new_objs = []
for obj in objs:
if not isinstance(obj.data, bpy.types.Mesh) or obj == cutter:
continue
matrix_i = obj.matrix_world.inverted()
bm_obj = bmesh.new()
bm_obj.from_mesh(obj.data)
bms = [bm_obj.copy()]
for co, no in planes:
co = matrix_i @ co
no = (matrix_i @ no).to_3d()
new_bms = []
for bm in bms:
bm1 = bm.copy()
bm2 = bm.copy()
geom = bm1.verts[:] + bm1.edges[:] + bm1.faces[:]
bisect1 = bmesh.ops.bisect_plane(
bm1, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_inner=True
)
if not bisect1["geom"] or not [g for g in bisect1["geom"] if isinstance(g, bmesh.types.BMFace)]:
new_bms.append(bm)
continue
edges = [g for g in bisect1["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
bmesh.ops.triangle_fill(bm1, use_dissolve=True, edges=edges)
geom = bm2.verts[:] + bm2.edges[:] + bm2.faces[:]
bisect2 = bmesh.ops.bisect_plane(
bm2, geom=geom, dist=0.0001, plane_co=co, plane_no=no, clear_outer=True
)
if not bisect2["geom"] or not [g for g in bisect2["geom"] if isinstance(g, bmesh.types.BMFace)]:
new_bms.append(bm)
continue
edges = [g for g in bisect2["geom_cut"] if isinstance(g, bmesh.types.BMEdge)]
bmesh.ops.triangle_fill(bm2, use_dissolve=True, edges=edges)
new_bms.append(bm1)
new_bms.append(bm2)
bms = new_bms
for bm in bms:
mesh = obj.data
new_obj = obj.copy()
new_obj.data = mesh.copy()
bm.to_mesh(new_obj.data)
for collection in obj.users_collection:
collection.objects.link(new_obj)
new_objs.append(new_obj)
return new_objs