1
$\begingroup$

My main goal is to separate multiple geometries the same way by vertice selection. All the objects differ only a little bit in their vertice position, because they only are deformed by already applied shape keys originating from the same origin object (amount and distribution of vertices are the same). My current strategy (corresponding to the bpy script below): Starting with a selection of vertices on the initial geometry, I would like to additionally import multiple very similar ones. By saving the vertice selection, the separation should be applied one object after another. (edited)

Please see my code below. Since it did not work to directly compare vertice objects, that initially have been stored to a list (previous version), I now try to compare list indexes.

Unfortunately, my example code only separates one object in the two geometrical parts:

import bpy
import os, glob

# script to separate same edit mode selection on multiple imported .fbx files in directory

class Import(object):
    def __init__(self):
        pass

    @staticmethod
    def import_fbxfiles_from_dir(path = "C:\\Users\\user\\Desktop\\test_parts\\"):
        os.chdir(path)
        for fbx in [f for f in os.listdir(path) if f.endswith(".fbx")]:
            print ("imported file: " + fbx)
            bpy.ops.import_scene.fbx(filepath=fbx, filter_glob=".fbx;", axis_forward="-Z", axis_up="Y")

class Separate_test(object):
    def __init__(self):
        pass

    def separate_initially_selected_verts(self):
        obj = bpy.context.scene.objects.active  # active object
        mesh = obj.data
        self.selected_vert_list_IDs = [key for key,v in enumerate(mesh.vertices) if v.select]
        print("selected verts:", self.selected_vert_list_IDs)

        bpy.ops.object.mode_set(mode='OBJECT')  # exit edit mode

        obNr = 0
        for ob in bpy.context.scene.objects:
            if ob.type == 'MESH':
                print("-- Object:", obNr, "/", len(bpy.context.scene.objects))
                bpy.context.scene.objects.active = ob

                # set to edit mode
                bpy.ops.object.mode_set(mode='EDIT')

                # select verts from list
                count_select = 1
                for key_ob,v in enumerate(bpy.context.scene.objects.active.data.vertices):
                    if key_ob in self.selected_vert_list_IDs:
                        count_select += 1
                        # print("vertices separated in obj: {0} of {1}".format(count_select, len(self.selected_vert_list_IDs)))
                        v.select = True

                bpy.ops.mesh.separate(type='SELECTED')
                bpy.ops.object.mode_set(mode='OBJECT')  # exit edit mode
            obNr += 1


if __name__ == "__main__":
    Import.import_fbxfiles_from_dir()
    separate_obj = Separate_test()

    separate_obj.separate_initially_selected_verts()
    input("press return to quit.")
$\endgroup$
3
  • $\begingroup$ That is really unclear. Please describe what your goals are clearly. $\endgroup$ Aug 30, 2018 at 16:08
  • $\begingroup$ Welcome to bse. Suggest for sake of question ditching the import (how can I test without that folder or without the obj's to import or what they look like???) and the input code (rarely used in blender) . To clarify for me: Lets say I have a default cone with base verts selected as active object. Would I then expect to separate out the corresponding base of a default cylinder?... or are all other objects also cones. $\endgroup$
    – batFINGER
    Aug 30, 2018 at 18:07
  • $\begingroup$ I significantly improved my question for better understanding. input code was for debugging only. Unfortunately, it is my first time scripting blender. $\endgroup$
    – boeni
    Aug 30, 2018 at 20:01

3 Answers 3

2
$\begingroup$

Perfect candidate for bmesh

Script to run in edit or object mode with "basis" mesh selected.

For each mesh object having same amount of vertices add a split by selection object. Selection: Default 'SELECT' uses selected vertices in basis. 'LOC' tags all verts within a tolerance of basis.

Set this up as a class such that it can be instanced with the basis object, and called with another.

import bpy
import bmesh


class diff:
    def split(self, scene, ob, select='SELECT'):
        me = ob.data
        bm = bmesh.new()
        bm.from_mesh(me)
        if select == 'SELECT':
            select = self.tag_select(bm)        
        elif select == 'LOCATION':
            select = self.tag_same_loc(bm)
        else:
            print("Error.. not in SELECT, LOCATION")
            select = False

        if select:
            obcopy = ob.copy()
            me = me.copy()
            # remove selected from one
            bmesh.ops.delete(bm, geom=[v for v in bm.verts if not v.tag], context=1)
            bm.to_mesh(me)
            obcopy.data = me
            scene.objects.link(obcopy)

    def tag_select(self, bm):
        for v0, v in zip(self.bm.verts, bm.verts):
            v.tag = v0.select
        return any(v.tag for v in bm.verts)

    def tag_same_loc(self, bm, TOL=1.0e-3):
        for v0, v in zip(self.bm.verts, bm.verts):
            v.tag = (v.co - v0.co).length < TOL
        return any(v.tag for v in bm.verts)        

    def __init__(self, scene, ob):
        me = ob.data
        if ob.mode == 'EDIT':
            bm = bmesh.from_edit_mesh(me)
        else:
            bm = bmesh.new()
            bm.from_mesh(me)
        self.bm = bm

if __name__ == "__main__":        
    context = bpy.context
    scene = context.scene
    ob = context.object
    d = diff(scene, ob)
    shapes = [o for o in scene.objects
                if o.type == 'MESH'
                and len(o.data.vertices) == len(o.data.vertices)]
    shapes.remove(ob) 

    for s in shapes:
        d.split(scene, s)
$\endgroup$
1
  • $\begingroup$ works like a charm. It would still be helpful, if the other part of the mesh would be separated too. $\endgroup$
    – boeni
    Sep 3, 2018 at 8:14
0
$\begingroup$

Looks like to me you have an Object, store the ID's of the selected vertices and divide all objects in the scene based on those. That can not work because other objects dont have vertices correspoding to those selected vertices.

I can only offer a solution that works before you apply complex transforms.

If you have an object and duplicate it, you can create ordered lists based on the 3D location of the vertices, that way no matter how transform, you can always find the "same" vertices. Finding the selected vertices in and object and comparing them to the list should be trivial if the ID doesn't change

$\endgroup$
4
  • $\begingroup$ Hi, thanks for your response. But since at least the list length of all the mesh objects should be the same, the script should be able to separate some 'other two parts' of the additionally loaded files in the for loop? I predict the error to be somewhere there (I am not very experienced with the blender API) and still have hope for the concept to work.. $\endgroup$
    – boeni
    Aug 31, 2018 at 7:52
  • $\begingroup$ I think my proposed way would work for any seperations based on vertices, of course only if the transforms are done after initialization step @boeni $\endgroup$
    – WhatAMesh
    Aug 31, 2018 at 7:54
  • $\begingroup$ @boeni It would also be possible to write an algorithm that finds the right vertices after the transfrom but thats too overcomplicated. The list length will be the same but I'm not sure the order of them within the list will $\endgroup$
    – WhatAMesh
    Aug 31, 2018 at 7:58
  • $\begingroup$ Thanks again. Exactly, I am not sure about the list index sequence either. That's why I would like to first make the script running, because it should separate something at all the parts (which it doesn't do right now without throwing an error) - eventhough possibly with wrong vertices. $\endgroup$
    – boeni
    Aug 31, 2018 at 8:24
0
$\begingroup$

It seems bpy.ops.mesh.separate operator requires some specific context to run. You could override the context for the operator or you could just make an operator that you can run in the 3d View panel so it is run in the correct context that would be something like this:

import bpy

class SeparateSameVertices(bpy.types.Operator):
    """Separate Same Vertices(by index) on selected objects"""
    bl_idname = "object.separate_same_vertices"
    bl_label = "Separate Same Vertices"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_vertices = []
        obj = bpy.context.scene.objects.active
        for every_vertex in obj.data.vertices:
            if every_vertex.select:
                selected_vertices.append(every_vertex.index)           
        selected_objects = bpy.context.selected_objects
        for every_object in selected_objects:  
            for faces in every_object.data.polygons:
                faces.select = False
            for edges in every_object.data.edges:
                edges.select = False       
            for every_vertex in every_object.data.vertices:
                if every_vertex.index in selected_vertices:
                    every_vertex.select = True
                else:
                    every_vertex.select = False
            bpy.context.scene.objects.active = every_object
            bpy.ops.object.editmode_toggle()
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
            bpy.ops.mesh.separate(type='SELECTED')
            bpy.ops.object.editmode_toggle()
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SeparateSameVertices)


def unregister():
    bpy.utils.unregister_class(SeparateSameVertices)


if __name__ == "__main__":
    register()

    # test call
 #   bpy.ops.object.simple_operator()

This uses the selection from the active object and separates all the selected objects based on that. You would need to run it and use the 'Separate Same Vertices' operator from the space menu in 3d viewport.

I think you might also be able to achieve this with bmesh in a more elegant way.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .