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)

8 Likes

Can you update this for Blender 2.92? The operation doesn’t show up on the search.

It does here:


I can’t seem to get that appear on my side. I’m supposed to install that as I normally install addon right? From the setting, I installed it and I activated it, yet it wont show both operation on search.

You need to create a file in your addons directory, name it for example select_by_face_count.py, put the code in that file, and then activate the add-on in Blender (it should be called “Select Islands by Face Count”). Then the two operators should be appearing in search in edit mode.

Sorry to bug you like this but after following your instruction precisely, I still cant get the operation to show up.

I tried restarting after activating it but it still wouldn’t show up.

Hm. Do you, by chance, have add-on filters enabled in that workspace? That could be the reason the op doesn’t show up. If you enable filters and then activate an add-on, you have to manually enable it in the filters.

I checked my workspace, the add-ons filter is unticked. Is it possible that Blender updates plugin with old API when you migrate from older version?

For me it still works fine. I called the file select_islands_by_face_count.py
I noticed sometimes, the .py file has to be a certain name to show up. Make sure the addon is also enabled

Sadly that is still not the case with me. Maybe I have add-on incompatibilities. I’ll report my findings later, if any.