Center of Mass

Discussions regarding modelling with SOFTIMAGE©
User avatar
mc_axe
Posts: 403
Joined: 12 Mar 2013, 18:44

Center of Mass

Post by mc_axe » 01 Dec 2018, 19:39

Hello evryone, anyone knows if we already have a tool to calculate center of mass for a closed mesh for example a rock ( assuming uniform density - same material everywhere) ?

Transform > 'Move center to vertices' preety much averages the possitions of vertices, so it depends on the topology,
A ball that has its top part subdivided a few times will have a center close to the top, while real center of mass should remain pretty mcuh in the same place.

I tried also rigid bodies where there is inertal properties > center of mass, but is litterally the same algorithm.

Here is a response that i found on google groups:
To find the center of gravity (or "centroid") of a polygonal mesh:
convert all of its faces to triangles, and average the centroids of
all of the triangles, weighted by the doubled area of each face.
Wikipedia calls it <a href="http://en.wikipedia.org/wiki/
Centroid#Centroid_by_geometric_decomposition">Centroid by Geometric
Decomposition</a>.

C = Centroid <vector>, A = (area of a face * 2)
R = face centroid = average of vertices making the face <vector>
C = [sum of all (A*R)] / [sum of all R]
Thx

User avatar
mc_axe
Posts: 403
Joined: 12 Mar 2013, 18:44

Re: Center of Mass

Post by mc_axe » 15 Dec 2018, 20:40

Hi all, tried to figure this out in ICE. *-:)

I sumed up all the polygon possitions of a mesh factored by their coresponding area, then i divided the result by the sum of total area of all polygons.
Then i fed the result on a null (host of the tree) as possition to visualize the new center (center of mass) but the null wont move for a reason (i can only showcase the value of Center of Mass).

^^The above is abit different to the proposed method (as seen in first post), i did not tried to implement centroids or something crazy that can solve evrything, my ICE knowledge is super limited.
Image
In the example i added a few loops in the right side of a 2x2x2 cube (with 2 subs in all axes) and i split once the top left then triangulate.
Move center to vertices would have taken the center at x: -0,0227 y: 0,0455 z: 1,1591, but the compound yields (0,0,1) which is the actual center of mass for a 2x2x2 cube positioned at (0,0,1). So regarldless of topology and dencity of components we can still find the "real" center.

*Requires freeze tranformations.
*Assumes a closed mesh no interiors and crazy stuff.
*Requires a triangulated mesh as input, that is because polygon possitions for triangles is their actual centroid which is not the case for example on a very random ngon


If anyone can help me so i can get the null to move that would be cool, thnx. :ymhug:

Kolya
Posts: 5
Joined: 03 Oct 2017, 19:17

Re: Center of Mass

Post by Kolya » 15 Dec 2018, 21:53

to move the null, you need to change null.kine.global attribute.
You do not have the required permissions to view the files attached to this post.

User avatar
mc_axe
Posts: 403
Joined: 12 Mar 2013, 18:44

Re: Center of Mass

Post by mc_axe » 16 Dec 2018, 22:41

Thx alot Kolya you the best! \m/

Next Ill try to figure out how it can work without a need to freeze translations , to update soon.

User avatar
myara
Posts: 380
Joined: 28 Sep 2011, 10:33

Re: Center of Mass

Post by myara » 17 Dec 2018, 07:59

I gave it a try and this is what I've got. It calculates the center of mass of the selected polygon mesh, creates a null and put the coordinates on it.

It calculates from the internal triangles so you don't need to triangulaze it, and it converts the values to global coordinates so you don't need to freeze the object either.

Code: Select all

#Python
xsi = Application
log = LogMessage

import math

def distanceBetween2points(A,B):
    '''
    Return Distance between 2 points in 3D Space
    distanceBetween2points([x,y,z],[x,y,z])
    '''
    return abs(math.sqrt(math.pow(B[0]-A[0],2) + math.pow(B[1]-A[1],2) + math.pow(B[2]-A[2],2)))

def getCenterOfMass(obj):
    geo = obj.ActivePrimitive.Geometry
    tris = geo.Triangles
    totalArea = 0
    sx=0
    sy=0
    sz=0
    for tri in tris:
        # Get Position of Triangle
        triPos = tri.PositionArray
        triPosX = triPos[0]
        triPosY = triPos[1]
        triPosZ = triPos[2]
        
        # Convert Position to Global and get Global Centroid of the triangle
        triPos_A = XSIMath.CreateVector3(triPosX[0], triPosY[0], triPosZ[0])
        triPos_B = XSIMath.CreateVector3(triPosX[1], triPosY[1], triPosZ[1])
        triPos_C = XSIMath.CreateVector3(triPosX[2], triPosY[2], triPosZ[2])
        triPos_A_G = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, triPos_A)
        triPos_B_G = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, triPos_B)
        triPos_C_G = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, triPos_C)
        
        triCenter_G = [(triPos_A_G.X + triPos_B_G.X + triPos_C_G.X)/3, (triPos_A_G.Y + triPos_B_G.Y + triPos_C_G.Y)/3, (triPos_A_G.Z + triPos_B_G.Z + triPos_C_G.Z)/3]

        # Get Triangle Area with Edges lenght and Semi Perimeter equation
        edgeA = distanceBetween2points([triPosX[0], triPosY[0], triPosZ[0]], [triPosX[1], triPosY[1], triPosZ[1]])
        edgeB = distanceBetween2points([triPosX[1], triPosY[1], triPosZ[1]], [triPosX[2], triPosY[2], triPosZ[2]])
        edgeC = distanceBetween2points([triPosX[2], triPosY[2], triPosZ[2]], [triPosX[0], triPosY[0], triPosZ[0]])
        semiPerimeter = (edgeA + edgeB+ edgeC) / 2
        area = (semiPerimeter*abs(semiPerimeter-edgeA)*abs(semiPerimeter-edgeB)*abs(semiPerimeter-edgeC)) ** 0.5
        
        sx += triCenter_G[0] * area
        sy += triCenter_G[1] * area
        sz += triCenter_G[2] * area

        totalArea += area

    # Calculate Center of Mass
    nulx = sx/totalArea
    nuly = sy/totalArea
    nulz = sz/totalArea
    
    return [nulx, nuly, nulz]

#------------------

# Selection    
obj = xsi.Selection(0)

# Create Null, and Assign coordinates
nul = xsi.ActiveSceneRoot.AddNull('nul')
centerOfMass = getCenterOfMass(obj)
nul.Kinematics.Global.posx = centerOfMass[0]
nul.Kinematics.Global.posy = centerOfMass[1]
nul.Kinematics.Global.posz = centerOfMass[2]
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
rray
Moderator
Posts: 1642
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

Re: Center of Mass

Post by rray » 17 Dec 2018, 14:24

Nice! For solid bodies then you would probably convert the mesh into tetrahedrons then use the same algorithm.

I looked a bit into it.. always thought that would be something almost as simple as triangulation, but it seems more complicated.
softimage resources section updated Feb 7 2019

User avatar
mc_axe
Posts: 403
Joined: 12 Mar 2013, 18:44

Re: Center of Mass

Post by mc_axe » 17 Dec 2018, 17:13

Kudos @Martin you are beyond awesome! :D :-bd

@rray incredible idea to break down the mesh in to tiny bits in order to solve all kinds of solid meshes, im going to try a voxelizer just out of curiosity at the weekend to see wat kind of resolution i can get :)

User avatar
myara
Posts: 380
Joined: 28 Sep 2011, 10:33

Re: Center of Mass

Post by myara » 18 Dec 2018, 04:12

Thanks, I hope you find it useful.

Well, although I think I'm right, I'm not completely sure about my math here, because it's not really my expertise.

A little break down of what I did.

Since you can access the internal triangle data from the object and the triangles positions (vertices positions), you don't need to triangulaze the object. You can do it even with Ngons.
So I did that with obj.ActivePrimitive.Geometry.Triangles and it's PositionArray.

I based my code from the physics equation where you calculate the center of mass between multiple bodies with different mass :
center of mass = sum ( position N * mass N ) / sum (mass N)

Each triangle will be considered an object, so we will have to find the center of mass of those triangles.

So to use that we need :
- centroid of each triangle (to have the position of each triangle)
- area of each triangle (to have the mass of the object)

For the centroid, I just used the average position ([(x+x+x)/3, ...].

To calculate the area I used the formula using the edges and semi perimeter because I though it was the simplest formula for this case:
area = square ( semi perimeter * ( semi perimeter - edge A) * ( semi perimeter - edge B) * ( semi perimeter - edge B) )

note : each semi perimeter - edge length must be positive, so I used abs() to convert it to an absolute number.

To have the edge I'm using calculating the distance between 2 vertices with this formula:
square ( (x2 - x1)**2 + (y2 -y1)**2 + (z2 - z1)**2 )

With that, I gather the " sum( position in 1 axis of the centroid * area of the triangle) " of all of the triangles in the object, and then divide it with the total area of the object.
And do it 3 times, 1 per axis.

To convert local position to global position I'm just using XSIMath.

I think it's really simple to get, but with getCenterOfMass(obj) you'll have the 3 coordinates, you use it as you want (make it the center of the object, creating a null, making it a vector, etc).
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
myara
Posts: 380
Joined: 28 Sep 2011, 10:33

Re: Center of Mass

Post by myara » 18 Dec 2018, 04:14

While I was writing my explanation, I realized I did something stupid.
Since we are working with only 1 object, I shouldn't be converting the position to global before doing the calculations, that would make it slower (converting to global is not a very fast operation, and this exponentially increase depending on the triangle cout).

Instead, the fastest approach would be working in local, and convert the result to global at the very end.

This should be much faster

Code: Select all

xsi = Application
log = LogMessage

import math

def distanceBetween2points(A,B):
    '''
    Return Distance between 2 points in 3D Space
    distanceBetween2points([x,y,z],[x,y,z])
    '''
    return abs(math.sqrt(math.pow(B[0]-A[0],2) + math.pow(B[1]-A[1],2) + math.pow(B[2]-A[2],2)))

def getCenterOfMass(obj):
    geo = obj.ActivePrimitive.Geometry
    tris = geo.Triangles
    totalArea = 0
    sx=0
    sy=0
    sz=0
    for tri in tris:
        # Get Position of Triangle
        triPos = tri.PositionArray
        triPosX = triPos[0]
        triPosY = triPos[1]
        triPosZ = triPos[2]
        
        triCenter = [sum(triPosX)/3, sum(triPosY)/3, sum(triPosZ)/3]

        # Get Triangle Area with Edges lenght and Semi Perimeter equation
        edgeA = distanceBetween2points([triPosX[0], triPosY[0], triPosZ[0]], [triPosX[1], triPosY[1], triPosZ[1]])
        edgeB = distanceBetween2points([triPosX[1], triPosY[1], triPosZ[1]], [triPosX[2], triPosY[2], triPosZ[2]])
        edgeC = distanceBetween2points([triPosX[2], triPosY[2], triPosZ[2]], [triPosX[0], triPosY[0], triPosZ[0]])
        semiPerimeter = (edgeA + edgeB+ edgeC) / 2
        area = (semiPerimeter*abs(semiPerimeter-edgeA)*abs(semiPerimeter-edgeB)*abs(semiPerimeter-edgeC)) ** 0.5
        
        sx += triCenter[0] * area
        sy += triCenter[1] * area
        sz += triCenter[2] * area

        totalArea += area

    # Calculate Center of Mass
    nulx = sx/totalArea
    nuly = sy/totalArea
    nulz = sz/totalArea
    
    # Convert the local position to Global
    nulpos = XSIMath.CreateVector3(nulx, nuly, nulz)
    nulpos_G = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, nulpos)
    
    return [nulpos_G.X, nulpos_G.Y, nulpos_G.Z]

#------------------

# Selection    
obj = xsi.Selection(0)

centerOfMass = getCenterOfMass(obj)

# Create Null, and Assign coordinates
nul = xsi.ActiveSceneRoot.AddNull('nul')
nul.Kinematics.Global.posx = centerOfMass[0]
nul.Kinematics.Global.posy = centerOfMass[1]
nul.Kinematics.Global.posz = centerOfMass[2]
PS: sorry for the double post, I though it would be easier to understand this way.

If you want to change the Center of the Object change the last part of the #Create Null... with this code:

Code: Select all

from win32com.client import constants as c
xsi.Translate(obj, centerOfMass[0], centerOfMass[1], centerOfMass[2], c.siAbsolute, c.siGlobal, c.siCtr, c.siXYZ)
M.Yara
Character Modeler | Softimage Generalist (sort of)