# Count the number of faces in a vertex group and its total area

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

• 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? Commented Jul 8, 2022 at 0:36

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)


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)


So i hope you get the idea.

• 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 Commented Jul 7, 2022 at 21:19
• 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. Commented Jul 8, 2022 at 0:31
• i updated my answer, assuming that you wanted the for loop to be on the 4th sub iteration level. Commented Jul 8, 2022 at 1:24
• 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 Commented Jul 11, 2022 at 15:10
• 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 Commented Jul 11, 2022 at 15:14