2
$\begingroup$

I will simplify my problem in this question. Let's say that I have two objects in my scene: a cube and a plane. I would like to translate the cube such that the position of the two down vertices on the right of the cube become the position of the two left vertices of the plane without deforming the cube (and the plane of course). So these vertices will share the same coordinates at the end of the process. I found this code that partially answer to my question, but in that case the effect would be the following:

enter image description here

while I need to have something like this:

enter image description here

Anyone has any idea of how to achieve this in Python?

EDIT: the objects are not aligned, they may be, but I am interested in the most general case. The practical problem is that I have a mesh of a pair of glasses and a mesh of a face. Now I want that 3 points from the bridge of the glasses share the position of three points of the nose of the face. Then, the rest of the glasses would moved property, with no stretching or deformation, in such a way that they are putting on the face. Furthermore, I can obtain the data from these two objects with the bmesh with something like this:

mesh = ob.data
mesh.vertices[1].select = True

I can also activate the debug mode with bpy.app.debug = True in the Python Shell and I can discover which is the position of the vertices I want to move. So for example they are:

mesh.vertices[43].select = True
mesh.vertices[56].select = True
mesh.vertices[78].select = True

in the array of the data of the glasses. Also I know where the vertices of the face are into the array of the face. Let's say they are something like:

mesh2 = ob2.data
mesh2.vertices[1].select = True
mesh2.vertices[10].select = True
mesh2.vertices[20].select = True

So, the situation I have is something like this:

enter image description here

The dimensions of the two objects don't need to be changed. It's just the position of the glasses that need to change according to the vertices of the bridge that has to be in the position of certain established vertices of the nose.

Hope now is more clear.

$\endgroup$

1 Answer 1

1
$\begingroup$

This method may not work for some cases since your question is not detailed enough (just in my opinion :) )

I am selecting 2 vertices (1 from the Cube and 1 from the Plane) that I assumed to be alligned by looking from your sight (as you see in the blender scene ) and getting their world coorinates. After that I am taking the difference of them to slide Cube. (Also I am assuming our objects are not rotated after created.) enter image description here

import bpy, bmesh
from mathutils import Vector

bpy.ops.object.select_all(action='DESELECT')

# set active object to Cube
bpy.context.scene.objects.active = bpy.data.objects["Cube"]
# go to EDIT mode
bpy.ops.object.editmode_toggle()

# get Cube's updated vertex data 
obj = bpy.data.objects["Cube"]
bm = bmesh.new()
bm = bmesh.from_edit_mesh(obj.data)
bm.verts.ensure_lookup_table()

# get world coordinate matrix for that object
mat = mat = obj.matrix_world

# get the right-bottom-closer vertex's world coordinates and save it to v1. By your sight , this vertex must have minimum x, y and z coordinates among the others.
# Additional info : if it is that vertex, its x coordinate is lower than upper adjacent , its y coordinate is lower than left adjacent ,its z coordinate is lower than further adjacent so I am searching these in the if and elif statements
for vert in bm.verts:
    minx = False
    miny = False
    minz = False
    for edge in vert.link_edges:
        vert2 = edge.other_vert(vert)
        if (mat*vert.co)[0]<(mat*vert2.co)[0]:
            minx = True
        elif (mat*vert.co)[1]<(mat*vert2.co)[1]:
            miny = True
        elif (mat*vert.co)[2]<(mat*vert2.co)[2]:
            minz = True
    if minx == True and miny == True and minz == True:
        v1 = mat*vert.co
bm.free()
# switch object mode and set active object to the plane to get its vertex data
bpy.ops.object.editmode_toggle()
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = bpy.data.objects["Plane"]
bpy.ops.object.editmode_toggle()

obj = bpy.data.objects["Plane"]
bm = bmesh.new()
bm = bmesh.from_edit_mesh(obj.data)
bm.verts.ensure_lookup_table()
mat = mat = obj.matrix_world

# get the left-closer vertex's world coordinates and save it to v2. By your sight , this vertex must have minimum x and maximum y coordinates among the others.
# Additional info : if it is that vertex, its x coordinate is lower than upper adjacent , its y coordinate is higher than left adjacent ,its x coordinate is lower than further adjacent so I am searching these in the if and elif statements
for vert in bm.verts:
    minx = False
    maxy = False
    for edge in vert.link_edges:
        vert2 = edge.other_vert(vert)
        if (mat*vert.co)[0]<(mat*vert2.co)[0]:
            minx = True
        elif (mat*vert.co)[1]>(mat*vert2.co)[1]:
            maxy = True
    if minx == True and maxy == True :
        v2 = mat*vert.co
bm.free()
# switch to object mode to move all cube at once , since we got every information we need
bpy.ops.object.editmode_toggle()


# move cube by their difference vector, so it will slide just by their difference value
bpy.data.objects["Cube"].location+= (v2-v1)

EDIT: I changed my code according to specifications now it gets 4 indexes (2 from cube(or glass), 2 from plane(or face) is enough to allign them)

import bpy, bmesh
from mathutils import Vector
import numpy as np , math

index_1_from_cube = 4
index_2_from_cube = 6

index_1_from_plane = 2
index_2_from_plane = 3

bpy.ops.object.select_all(action='DESELECT')

# set active object to Cube
bpy.context.scene.objects.active = bpy.data.objects["Cube"]
# go to EDIT mode
bpy.ops.object.editmode_toggle()

# get Cube's updated vertex data 
obj = bpy.data.objects["Cube"]
bm = bmesh.new()
bm = bmesh.from_edit_mesh(obj.data)
bm.verts.ensure_lookup_table()

# get world coordinate matrix for that object
mat = mat = obj.matrix_world

# get world coordinates of 2 vertices from cube that is planned to allign

v1 = mat*bm.verts[index_1_from_cube].co
v1_for_rotate = mat*bm.verts[index_2_from_cube].co
bm.free()
# switch object mode and set active object to the plane to get its vertex data
bpy.ops.object.editmode_toggle()
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = bpy.data.objects["Plane"]
bpy.ops.object.editmode_toggle()

obj = bpy.data.objects["Plane"]
bm = bmesh.new()
bm = bmesh.from_edit_mesh(obj.data)
bm.verts.ensure_lookup_table()
mat = mat = obj.matrix_world


# get world coordinates of 2 vertices from plane that is planned to allign
v2 = mat*bm.verts[index_1_from_plane].co
v2_for_rotate = mat*bm.verts[index_2_from_plane].co
bm.free()
# switch to object mode to move all cube at once , since we got every information we need
bpy.ops.object.editmode_toggle()


# move cube by their difference vector, so it will slide just by their difference value
# but after this operation only the 2 vertices will be alligned, 
# so we need to rotate it according to second set of points.
bpy.data.objects["Cube"].location+= (v2-v1)

#select the cube again
bpy.context.scene.objects.active = bpy.data.objects["Cube"]
bpy.data.objects["Cube"].select = True

# we will rotate the cube around the first point since the first points are already alligned (we don't need to change this coordinate of cube)
bpy.context.scene.cursor_location = v2
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')

# get unit vectors in order to find a angle and rotation axis
unit_vec1 = (v1_for_rotate - v1 ) / np.linalg.norm(v1_for_rotate - v1 )
unit_vec2 = (v2_for_rotate - v2 ) / np.linalg.norm(v2_for_rotate - v2)


rotation_axis = (np.cross(unit_vec1, unit_vec2))
rotation_degree = np.arccos(np.clip(np.dot(unit_vec1, unit_vec2), -1.0, 1.0))

# rotate the object around first alligned points
bpy.ops.transform.rotate(value=rotation_degree, axis=rotation_axis, constraint_axis=(False, False, False))
$\endgroup$
3
  • $\begingroup$ Also, if you can share your blend file, I think I can help better. $\endgroup$ Apr 19, 2017 at 0:08
  • $\begingroup$ This helped, partially. I added more details to the question, so maybe now is more clear. The point of your solution is nice, just a pair of notes: first of all I don't need a loop to find the vertices as you did because I already know who day are (thanks to the debug mode). Secondly, should't I declare v1 and v2 like v1 = Vector((0,0,0)) before the loop for having this code works, right? $\endgroup$
    – Rexam
    Apr 20, 2017 at 8:01
  • $\begingroup$ Yes, probably it will work. I will edit my answer according to your edition:D $\endgroup$ Apr 21, 2017 at 16:38

You must log in to answer this question.

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