Select all meshes with same number of faces in edit mode

Hello,

is there a way to select all meshes with the same number of vertices or faces in Blender? Let’s assume we have and object with like 50 planes in it (hair cards) and 10 of them have 44 faces over all and are not connected to each other of course. How would someone select one and then let blender auto select all poly cards with the same number of faces? I would be surprised that no such function exists. Would love such a function and even pay a bit of money for a python script or addon which would be able to do that. Since I’m unable to do that or even found one for Blender 2.8x

I’m new I hope this is the correct sub topic.

Thanks in advance!

If they’re at all similar topologically and are within one mesh object, you could try Select Similar -> Face Regions. If not or if that one doesn’t cut it, a script could indeed be written. Feel free to PM me with details (preferably example object(s)).

1 Like

And here’s an add-on for 2.8x. It contains two operators:

  • Select Islands By Face Count - pick at least one face on any island, and run it. It will select all islands that have the same amount of faces as the one(s) you picked face(s) on
  • Separate By Face Count - separates current mesh into distinct objects, each containing face islands with matching face count (no need to select anything at all).

This has no UI, search for operators by name using the search function.

Thanks to @Shiroi_Akuma for input and testing!

bl_info = {
    "name": "Select Islands by Face Count",
    "author": "Stan_Pancakes",
    "version": (1, 1, 0),
    "description": "",
    "blender": (2, 80, 0),
    "location": "",
    "warning": "",
    "category": "Mesh",
}

import bpy
import bmesh
from collections import defaultdict

def adjacent_faces(face):
    """Iterates all faces adjacent to `face`"""
    for loop in face.loops:
        radial = loop.link_loop_radial_next
        while radial != loop:
            result = radial.face
            radial = radial.link_loop_radial_next
            yield loop.edge, result

def face_islands(bm, faces):
    """Given a sequence of faces, iterates all face islands those faces are part of

    Yields (island, selected) where island is a list of faces, and selected - number of selected faces in the island.
    """
    tags = [0] * len(bm.faces)

    def mark(f):
        tags[f.index] += 1
        return f

    for face in filter(lambda f: not tags[f.index], faces):
        island = [mark(face)]
        extend = island.extend
        num_selected = 0
        for af in island:
            num_selected += int(af.select)
            extend(mark(f) for _, f in adjacent_faces(af) if not tags[f.index])
        yield island, num_selected

class SelectIslandsByFaceCount(bpy.types.Operator):
    """Selects face islands matching face count of islands that have selected face(s)"""

    bl_idname = "mesh.select_islands_by_face_count"
    bl_label = "Select Islands by Face Count"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return context.mode == 'EDIT_MESH'

    def execute(self, context):
        mesh = context.object.data

        if not mesh.total_face_sel:
            return {'CANCELLED'}

        bm = bmesh.from_edit_mesh(mesh)

        islands = defaultdict(list)
        search_counts = set()

        for island, selected in face_islands(bm, bm.faces):
            size = len(island)
            islands[size].append(island)
            if selected:
                search_counts.add(size)

        bpy.ops.mesh.select_all(action='DESELECT')
        for count in search_counts:
            for island in islands.get(count, []):
                for face in island:
                    face.select = True

        bm.select_flush_mode()
        bmesh.update_edit_mesh(mesh, False, False)

        return {'FINISHED'}

class SeparateByFaceCount(bpy.types.Operator):
    """Separates all islands with matching face counts into objects"""

    bl_idname = "mesh.separate_by_face_count"
    bl_label = "Separate By Face Count"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return context.mode == 'EDIT_MESH'

    def execute(self, context):
        mesh = context.object.data

        if not mesh.total_face_sel:
            return {'CANCELLED'}

        bm = bmesh.from_edit_mesh(mesh)

        islands = defaultdict(list)

        for island, selected in face_islands(bm, bm.faces):
            size = len(island)
            islands[size].append(island)

        # Leave largest islands in the current object
        for size in sorted(islands.keys())[:-1]:
            bpy.ops.mesh.select_all(action='DESELECT')
            for island in islands[size]:
                for face in island:
                    face.select = True
            bm.select_flush_mode()
            bpy.ops.mesh.separate(type='SELECTED')

        return {'FINISHED'}


def register():
    bpy.utils.register_class(SelectIslandsByFaceCount)
    bpy.utils.register_class(SeparateByFaceCount)

def unregister():
    bpy.utils.unregister_class(SeparateByFaceCount)
    bpy.utils.unregister_class(SelectIslandsByFaceCount)

1 Like