0
$\begingroup$

I need to count in my scene for each possible vertex group (in each mesh) the number of faces of that vertex group and its total area (the sum of the areas of its faces). I made this first solution even with your help but it is still not enough, in my addon 5-6 seconds are wasted to perform this function and I would like something faster, consider that the test was done on a mesh with 230,000 vertices and 500,000 faces, how can I make it more efficient?

def count_face_area(obj, num_groups):
    group_faces = [0] * num_groups #list with the total number of faces for each vertex group
    area = [0] * num_groups #list with the total area for each vertex group
    
    vertices = obj.data.vertices
    polygons = obj.data.polygons

    for face in polygons:
        group_counts = [0] * num_groups
        for vertex_idx in face.vertices:
            vertex = vertices[vertex_idx]
            for vgroup in vertex.groups:
                group_counts[vgroup.group] += 1
                
        for group_idx, count in enumerate(group_counts):
            if count == len(face.vertices):
                group_faces[group_idx] += 1
                area[group_idx] += face.area
                break

    return group_faces, area

Imagine that this function is inside a for loop that iterates through all the meshes of my scene so the obj parameter will be a mesh at each loop (obj = bpy.data.objects["mymesh"]), num_groups instead is the number of total vertex group

$\endgroup$
1
  • $\begingroup$ your code in this question is not properly indented. i think that last for loop needs to be inside the main for loop. but how deep inside should it be. should it be a fourth level loop? $\endgroup$ Jul 8, 2022 at 0:36

1 Answer 1

0
$\begingroup$

One problem I found in your code is that you are repetitively accessing the same properties (dot notation) over and over again that is additional overhead per iteration which can reduce performance. Run the script and notice that the modified function i made called count_face_area_40_percent_faster stores several properties in new variables which leads to better performance than the original count_face_area function. Compare the time elapsed which you can see in the system console.

I was able to decrease the processing time by 40% just by declaring the following line outside of the for loop:

vertices = obj.data.vertices

Here to demonstrate the performance difference with a simple for loop.

import bpy
import time

iter = 500000
x = None

def test_slow(obj):
    for i in range(iter):
        x = bpy.context.object.data.vertices[0]

def test_fast(obj):
    vertices = obj.data.vertices

    for i in range(iter):
        x = vertices[0]

print("start ====>")

start = time.time()
test_slow(bpy.context.object)
end = time.time()
print("elapsed time =======> test_slow:", end - start)

start = time.time()
test_fast(bpy.context.object)
end = time.time()
print("elapsed time =======> test_fast:", end - start)

enter image description here

import bpy
import time

def count_face_area(obj, num_groups):
    group_faces = [0] * num_groups #list with the total number of faces for each vertex group
    area = [0] * num_groups #list with the total area for each vertex group

    for face in obj.data.polygons:
        group_counts = [0] * num_groups
        for vertex_idx in face.vertices:
            vertex = obj.data.vertices[vertex_idx]
            for vgroup in vertex.groups:
                group_counts[vgroup.group] += 1
                for group_idx, count in enumerate(group_counts):
                    if count == len(face.vertices):
                        group_faces[group_idx] += 1
                        area[group_idx] += face.area
                        break

    return group_faces, area


def count_face_area_40_percent_faster(obj, num_groups):
    group_faces = [0] * num_groups #list with the total number of faces for each vertex group
    area = [0] * num_groups #list with the total area for each vertex group

    vertices = obj.data.vertices

    for face in obj.data.polygons:
        group_counts = [0] * num_groups
        for vertex_idx in face.vertices:
            vertex = vertices[vertex_idx]
            for vgroup in vertex.groups:
                group_counts[vgroup.group] += 1
                
                for group_idx, count in enumerate(group_counts):
                    if count == len(face.vertices):
                        group_faces[group_idx] += 1
                        area[group_idx] += face.area
                        break

    return group_faces, area

print("start ====>")

start = time.time()
count_face_area(bpy.context.object, 0)
end = time.time()
print("elapsed time =======>   original:", end - start)

start = time.time()
count_face_area_40_percent_faster(bpy.context.object, 0)
end = time.time()
print("elapsed time =======> 40% faster:", end - start)

enter image description here

So i hope you get the idea.

$\endgroup$
13
  • $\begingroup$ it seems that in your code there is an indentation problem, in fact your solution is more efficient but the data that is returned to me is completely wrong, if I put everything inside the "for" loop that iterates all the faces then the data is correct but I lose efficiency again and the situation does not change with respect to my solution $\endgroup$
    – luke_mcp
    Jul 7, 2022 at 21:19
  • $\begingroup$ ah i know why, because your code has the indentation problem. I can see your for group_idx, count in enumerate(group_counts): loop is not indented to be included in the main for loop. try copy your code in your question and paste it into blender. that is the one that is incorrect. $\endgroup$ Jul 8, 2022 at 0:31
  • $\begingroup$ i updated my answer, assuming that you wanted the for loop to be on the 4th sub iteration level. $\endgroup$ Jul 8, 2022 at 1:24
  • $\begingroup$ I'm sorry in fact I had not seen that I made a mistake in reporting the code, I corrected it, despite this your solution still does not give me efficient results $\endgroup$
    – luke_mcp
    Jul 11, 2022 at 15:10
  • $\begingroup$ then ur doing something wrong. have u tried the sample code? it clearly is way faster. i can see obj.data.vertices is still in your loop. it needs to be outside declared outside the loop $\endgroup$ Jul 11, 2022 at 15:14

You must log in to answer this question.

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