2
$\begingroup$

I have a lot of calculations I need to do on meshes using global coordinates (distances between vertices, surface areas regions, etc.), and my script is getting slow. I am wondering if there are any useful strategies to avoid having to constantly re-multiply vertex coordinates by matrix_world every time I need to loop over a set of vertices. For example, it would be great if there were an "operate in world coordinates" mode, or a way to convert all local coordinates to their global coordinates.

# loop outline that is performed often
mat1 = ob1.matrix_world
mat2 = ob2.matrix_world
for v1_ind in verts1:
    v1 = mat1 * ob1.data.vertices[v1_ind].co
    for v2_ind in verts2:
        v2 = mat2 * ob2.data.vertices[v2_ind].co
        # computations

Further, is a way to use Blender's polygon.area to obtain the area of a face in global coordinates, or do I have to calculate it myself using the global coordinates of each vertex defining the face (slower)?

Thanks!

$\endgroup$
1
$\begingroup$

You could simply apply transformation, scale and rotation to the object before doing vertex calculations. This transforms all vertices to world coordinates (and pretty fast as well!).

bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

You probably need to copy the object first if you want to keep the old transformations though.

$\endgroup$
1
  • $\begingroup$ Thanks Parkuhr. In my case I only ever care about the global coordinates, so this is the best solution for me. $\endgroup$
    – Animik
    Jan 29 '16 at 11:06
2
$\begingroup$

You should cache the result of the inner loop.

verts2 = [mat2 * ob2.data.vertices[v2_ind].co for v2_ind in verts2]

and might do this for the outer loop as well

verts1 = [mat1 * ob1.data.vertices[v1_ind].co for v1_ind in verts1]

Generally you might gain performance by keeping python objects rather than doing an attribute / index lookup each time.

Tested with suzanne subdivided 3 times:

import bpy
import timeit
log = bpy.data.texts['log']
log.clear()

stmt_setup = """
import bpy
obj = bpy.context.active_object
vertices = obj.data.vertices
mat_world = obj.matrix_world
indices = range(len(vertices))"""

stmt_vertex_iter = """
verts_world = [mat_world * v.co for v in vertices]"""

stmt_index_lu = """
verts_world = [mat_world * obj.data.vertices[i].co for i in indices]"""

time_lc = timeit.timeit(stmt=stmt_vertex_iter, setup=stmt_setup, number=100)
time_il = timeit.timeit(stmt=stmt_index_lu,    setup=stmt_setup, number=100)

log.write("Vertex Iterator %.4f \n" % time_lc)
log.write("Index Look Up %.4f \n" % time_il)

yields

Vertex Iterator 2.5746 
Index Look Up 4.8094 
$\endgroup$
2
  • $\begingroup$ Discussion on Blenderartists $\endgroup$ Jan 24 '16 at 22:10
  • $\begingroup$ Thanks pink vertex, storing the vertices as a python list is undoubtedly the most general solution to this problem. (And I'll vote for your answer as soon as I have enough reputation for the system to allow it...) $\endgroup$
    – Animik
    Jan 29 '16 at 11:05
2
$\begingroup$

transform methods

Given the accepted answer, this can be done via

ob = context.object
me = ob.data
me.transform(ob.matrix_world)
ob.matrix_world = Matrix() # set to identity since verts now global

Similarly with bmesh and particularly handy for calculating face areas etc. The transformed bmesh need not be written back to the mesh, just as a way to keep track of geometry in global (or otherwise matrix transformed) coordinates.

bm = bmesh.new()
bm.from_mesh(me)
bm.transform(ob.matrix_world) # bm vert coords now global

total_global_face_area = sum(f.calc_area() for f in bm.faces)

Numpy.

Numpy can be used for quick matrix multiplication over vectors in a list. See Replace matrix @ vector list comprehensions with something more efficient

to apply the global transform to all vertices

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.