# How to select and separate same vertices as current selection on copied, very similar geometry

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()

• That is really unclear. Please describe what your goals are clearly. – Martynas Žiemys Aug 30 '18 at 16:08
• 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. – batFINGER Aug 30 '18 at 18:07
• I significantly improved my question for better understanding. input code was for debugging only. Unfortunately, it is my first time scripting blender. – boeni Aug 30 '18 at 20:01

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

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)

• works like a charm. It would still be helpful, if the other part of the mesh would be separated too. – boeni Sep 3 '18 at 8:14

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

• 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.. – boeni Aug 31 '18 at 7:52
• 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 – WhatAMesh Aug 31 '18 at 7:54
• @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 – WhatAMesh Aug 31 '18 at 7:58
• 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. – boeni Aug 31 '18 at 8:24

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.