# How can I calculate and draw the center of mass of various objects, with Python?

I am using a Python script to render the below scene in Blender. Now, I am trying to pack them using the piston on the right hand side to make it look like this: The center of mass before packing will be roughly at the center of the box and it will be shifted to a different point after packing the objects. The question I have is, how to do the center of mass drawing in the Blender and how to calculate it in the Blender using a Python script. Does Blender have an inbuilt feature for doing this? I have checked online and the closest I have found is this.

http://www.wildpockets.com/sites/all/themes/ninesixty/exporterstemp/wildPocketsBlenderExporter-v0.6.py

• Using Blender 2.74 on OsX
• Using Blender Game Engine to do the packing

A bit late to answer this, but I recently had to compute the center of gravity of various objects, so here is what I got.

1) "origin to center of mass" does not put the origin to the center of mass

What this command does is to consider the object as an empty shell, i.e. it computes the centroid of all faces. This is not the same thing as the center of mass of a solid object with constant density.

2) as far as I know there is no builtin feature to compute the center of mass of a solid object.

See this answer for a basic script that does the job.

3) to be useful, center of mass computation has to consider various ways of defining actual mass.

Some objects will represent some material with constant density (a bowling ball is indeed a mass of plastic). Some will rather be the abstraction of a complex object of known weight (for instance the engine of a car). Lastly, some curves might represent ropes, rods or wires with a known linear weight (the spring of a clamp)

Based on all this, I designed a small script that does compute the center of mass of a collection of heterogenous objects.

3 kinds of objects can take part in the computation:

• meshes with a custom weight attribute
• meshes with a material that has a custom density attribute
• curves with a material that has a custom linear weight attribute

To use it, you have to define the various attributes and materials required, then you just select all the objects you want (selecting "weightless" objects, empties, whatever will do no harm) and run the script. It will place the 3D cursor at the resulting CG location.

As an illustration, see how the CG moves: Battery placed further back Landing gear down I used millimeters and grams as units in my case, so if you want to switch to imperial system or change the scale you'll have to tweak a few constants. See the script for details.

import bpy
import mathutils
from mathutils import Vector

def triangles (verts):
"""enumerate triangles in a face"""
for i in range (1, len(verts)-1):
yield (verts, verts[i], verts[i+1])

def cg_mesh (obj):
"""center of gravity and mass of a mesh"""

center = Vector()
volume = 0
mesh = obj.to_mesh (bpy.context.scene, True, 'PREVIEW')
for face in mesh.polygons:
f = face.vertices
for t in triangles (f):
a,b,c = (mesh.vertices[v].co for v in t)
v = a.cross(b).dot(c) / 6
center += v * (a+b+c) / 4
volume += v

if volume == 0: print ("ZERO VOLUME", obj.name)
else          : center /= volume
bpy.data.meshes.remove(mesh)

# To make a mesh heavy
# define a "weight" (in grams) custom property at object level, or
# define a "density" (in g/cm3) custom property in associated material
if 'weight' in obj:
mass = obj['weight']
elif obj.active_material and 'density' in obj.active_material:
mass = volume * obj.active_material['density'] / 1000
else:
mass = 0
return center, mass

def cg_curve (obj):
"""center of gravity and mass of a curve"""

# to make a curve heavy,
# define a "linear weight" (in g/m) custom property in associated material
try:
linear_weight = obj.active_material['linear weight'] / 1000
except:
print ("NO MATERIAL FOR CURVE", obj.name)
return Vector(), 0

# copy the curve and its modifiers
bones = obj.copy()
bones.data = obj.data.copy() # copy curve
curve = bones.data

# reduce curve to skeleton
curve.bevel_object = None
curve.taper_object = None

# convert to mesh, applying modifiers
mesh = bones.to_mesh (bpy.context.scene, True, 'PREVIEW')

# compute skeleton length and center of mass
len = 0
center = Vector()
for segment in mesh.edges:
a, b = (mesh.vertices[v].co for v in segment.vertices)
l = (a-b).length
center += l * (a+b) # / 2 (median point) taken out of the loop
len += l
print (l,a,b)
bpy.data.meshes.remove (mesh)

bpy.data.objects.remove (bones)
bpy.data.curves.remove (curve)

return center/len/2, len * linear_weight

def cg_switch (obj):
if   obj.type == 'MESH' : return cg_mesh (obj)
elif obj.type == 'CURVE': return cg_curve(obj)
else                    : return Vector(), 0

def cg (obj):
center, mass = cg_switch (obj)
return obj.matrix_world * center, mass

center = Vector()
weight = 0
for obj in bpy.context.selected_objects:
c, w = cg (obj)
center += c * w
weight += w
if weight != 0:
bpy.context.scene.cursor_location = center/weight
print (center/weight, weight)


In Blender 2.79 you can move the origin of an object to the center of mass, with the choice of computing it from the surface or the volume :

bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME')
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')


"ORIGIN_CENTER_OF_MASS": Sets object origin to Center of Mass (Surface), Calculate the center of mass from the surface area.

"ORIGIN_CENTER_OF_VOLUME": Sets object origin to Center of Mass (Volume), Calculate the center of mass from the volume (must be manifold geometry with consistent normals).

See the docs here