Using Python I want to split a mesh into tiles. All the solutions to this question rely on bpy.ops.mesh.separate(type='LOOSE'). But this leads to problems in the following mesh:
I need 10 objects, named bisect-1, bisect-2 etc. But after bisecting with the code below the overhanging cubes on top become disconnected from the surface, and will end up in their own object as a result of bpy.ops.mesh.separate(type='LOOSE'). I end up with 36 objects instead of 10.
How to bisect a mesh into n parts, without separate 'LOOSE'?
import bpy, bmesh from bpy import context as C from mathutils import Vector def bounds(obj, local=False): local_coords = obj.bound_box[:] om = obj.matrix_world if not local: worldify = lambda p: om * Vector(p[:]) coords = [worldify(p).to_tuple() for p in local_coords] else: coords = [p[:] for p in local_coords] rotated = zip(*coords[::-1]) push_axis = [] for (axis, _list) in zip('xyz', rotated): info = lambda: None info.max = max(_list) info.min = min(_list) info.distance = info.max - info.min push_axis.append(info) import collections originals = dict(zip(['x', 'y', 'z'], push_axis)) o_details = collections.namedtuple('object_details', 'x y z') return o_details(**originals) object_details = bounds(C.object, True) bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(C.object.data) edges = [] # split the model into parts along y-axis, with steps of 10 in local coordinate space (this ignores the scale property, so make sure to apply it before) for i in range(int(round(object_details.y.min)), int(round(object_details.y.max)), 10): ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,i,0), plane_no=(0,1,0)) bmesh.ops.split_edges(bm, edges=[e for e in ret['geom_cut'] if isinstance(e, bmesh.types.BMEdge)]) bmesh.update_edit_mesh(C.object.data) bpy.ops.mesh.separate(type='LOOSE') bpy.ops.object.mode_set(mode='OBJECT') Edit: code changes after comment from batFINGER. Object isn't yet split after bisecting. But the 36 separate islands are identified. Now I need to group the islands based on the bisection coordinates, make separate objects and name them appropriately. But I'm not sure how to approach this.
import bpy, bmesh from bpy import context as C from mathutils import Vector # https://blender.stackexchange.com/questions/75332/how-to-find-the-number-of-loose-parts-with-python/105142#105142 def walk_island(vert): ''' walk all un-tagged linked verts ''' vert.tag = True yield(vert) linked_verts = [e.other_vert(vert) for e in vert.link_edges if not e.other_vert(vert).tag] for v in linked_verts: if v.tag: continue yield from walk_island(v) def get_islands(bm, verts=[]): def tag(verts, switch): for v in verts: v.tag = switch tag(bm.verts, True) tag(verts, False) ret = {"islands" : []} verts = set(verts) while verts: v = verts.pop() verts.add(v) island = set(walk_island(v)) ret["islands"].append(list(island)) tag(island, False) # remove tag = True verts -= island return ret # https://blender.stackexchange.com/questions/32283/what-are-all-values-in-bound-box def bounds(obj, local=False): local_coords = obj.bound_box[:] om = obj.matrix_world if not local: worldify = lambda p: om * Vector(p[:]) coords = [worldify(p).to_tuple() for p in local_coords] else: coords = [p[:] for p in local_coords] rotated = zip(*coords[::-1]) push_axis = [] for (axis, _list) in zip('xyz', rotated): info = lambda: None info.max = max(_list) info.min = min(_list) info.distance = info.max - info.min push_axis.append(info) import collections originals = dict(zip(['x', 'y', 'z'], push_axis)) o_details = collections.namedtuple('object_details', 'x y z') return o_details(**originals) object_details = bounds(C.object, True) bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(C.object.data) edges = [] # split the model into parts along y-axis, with steps of 10 in local coordinate space (this ignores the scale property, so make sure to apply it before) for i in range(int(round(object_details.y.min)), int(round(object_details.y.max)), 10): ret = bmesh.ops.bisect_plane(bm, geom=bm.verts[:]+bm.edges[:]+bm.faces[:], plane_co=(0,i,0), plane_no=(0,1,0)) bmesh.ops.split_edges(bm, edges=[e for e in ret['geom_cut'] if isinstance(e, bmesh.types.BMEdge)]) bmesh.update_edit_mesh(C.object.data) # bpy.ops.mesh.separate(type='LOOSE') islands = [island for island in get_islands(bm, verts=bm.verts)["islands"]] print("Islands:", len(islands)) print([len(i) for i in islands]) bpy.ops.object.mode_set(mode='OBJECT') 