# How to compute the centroid of a mesh with triangular faces without using Blender's built-in functions?

I do not want to use Blender's built-in functions for computing centroids as explained here as it seems that they do not give me the kind of centroid I expect to get. I want to compute a new centroid for my meshes given the following description. First, I want to compute the centers of faces (triangle) of a mesh centroid of a mesh. Then I need to compute the faces area. The new centroid is the average of the mesh faces' centers, weighted by their area. How can I do this using Blender's Python API?

• I've assumed by without using Blender built in functions you mean without using the transform origin operators. Feb 22, 2018 at 16:34

Bmesh approach

The bmesh module has for faces the methods to calculate centre and area.

There are three methods to calculate the centre of the face.

calc_center_bounds()

Return bounds center of the face.
Returns:  a 3D vector.
Return type:  mathutils.Vector


calc_center_median()

Return median center of the face.
Returns:  a 3D vector.
Return type:  mathutils.Vector


calc_center_median_weighted()

Return median center of the face weighted by edge lengths.
Returns:  a 3D vector.
Return type:  mathutils.Vector


I've used calc_center_median() for $x_i$ and BMFace.calc_area() for the weight $w_i$ for each face of index $i$ the weighted mean is

$$\tilde x = \frac {w_0x_0 + w_1x_1 + ... + w_nx_n}{w_0 + w_1 + ... + w_n}$$

import bpy
import bmesh
from mathutils import Vector
context = bpy.context

ob = context.object
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)

weights = [f.calc_area() for f in bm.faces]
weighted_centres = [f.calc_area() * f.calc_center_median()
for f in bm.faces]

local_weighted_mean = sum(weighted_centres, Vector()) / sum(weights)
print(local_weighted_mean)

# set as new origin
for v in bm.verts:
v.co -= local_weighted_mean
mw = ob.matrix_world
mw.translation = mw * local_weighted_mean
bm.to_mesh(me)
me.update()


Result of running on newly added Suzanne. Scene cursor shows old origin.

Note: if modifiers are used on the object to triangulate, or subdivide, can (for example) use

bm.from_object(ob, context.scene, render=True)


to calculate weighted origin of modified mesh.

You can do that with some simple math

import bpy
import mathutils

def unit_normal(vert1, vert2, vert3):
determinant = mathutils.Vector((
mathutils.Matrix((
(1.0, vert1[1], vert1[2]),
(1.0, vert2[1], vert2[2]),
(1.0, vert3[1], vert3[2]))).determinant(),
mathutils.Matrix((
(vert1[0], 1.0, vert1[2]),
(vert2[0], 1.0, vert2[2]),
(vert3[0], 1.0, vert3[2]))).determinant(),
mathutils.Matrix((
(vert1[0], vert1[1], 1.0),
(vert2[0], vert2[1], 1.0),
(vert3[0], vert3[1], 1.0))).determinant()))
return determinant.normalized()

def calc_area_of_polygon(mesh, polygon):
total = mathutils.Vector((0.0,0.0,0.0))
last_vertex = mathutils.Vector(mesh.vertices[polygon.vertices[-1]].co)
for vertex in polygon.vertices:
this_vertex = mathutils.Vector(mesh.vertices[vertex].co)
total += this_vertex.cross(last_vertex)
last_vertex = this_vertex
result = total.dot(unit_normal(
mesh.vertices[polygon.vertices[0]].co,
mesh.vertices[polygon.vertices[1]].co,
mesh.vertices[polygon.vertices[2]].co,
))
return abs(result/2)
def calc_center_of_polygon(mesh: bpy.types.Mesh, polygon: bpy.types.MeshPolygon):
result = mathutils.Vector()
for vertex in polygon.vertices:
result += mathutils.Vector(mesh.vertices[vertex].co)
return result / len(polygon.vertices)

def calc_center(mesh: bpy.types.Mesh):
total_weight = 0.0
total_center = mathutils.Vector()
for polygon in mesh.polygons:
weight = calc_area_of_polygon(mesh, polygon)
total_weight += weight
total_center += weight * calc_center_of_polygon(mesh, polygon)

print(calc_center(bpy.data.meshes['Cube']))

• Your code actually has some bugs. It gives very incorrect numbers.
– Amir
Feb 27, 2018 at 6:40
• I reviewed your changes. But please be aware that you need to use the bpy.types.Mesh object like in the last line of the example. In your review you wanted to use it by using bpy.types.Object which is not my intention so I rejected it. Sorry if my try gave some bad results. Feb 27, 2018 at 7:23
• Oops, apologies on my part for the se approval, thought it was mostly Re: vertex vs vertice "crime" didn't get as far as to see the mesh.data edit. Will check more carefully in future. Feb 28, 2018 at 10:29
• @batFINGER no problem. will update the vertex stuff :-) Feb 28, 2018 at 10:38

Update for newer versions of blender (I'm running 3.6)

mw.translation = mw * local_weighted_mean
mw.translation = mw @ local_weighted_mean