barycentric coordinates – part 1

david | maya,OpenMaya,python,tutorials | Monday, March 9th, 2015

Barycentric coordinates are useful when you want to transfer data between meshes that have different topology because they provide the basis for some simple interpolation. Take smooth skin binding for example. It is a commmon workflow, when painting weights, to start with a low resolution version of your mesh. When you are happy with the weighting you can use maya's copySkinWeights command to transfer those weights from the low resolution mesh to the detailed high resolution mesh, which may even be made up of several pieces. copySkinWeights interpolates the weights from the verts on the lo res mesh and copies the result to the hi res mesh. The smooth result you get with this workflow is usually something that would have been very difficult to achieve if you had tried to paint the weights directly on the hires mesh.

The math behind the interpolation is fairly simple and uses "barycentric coordinates". You do not need to actually know the math to use barycentric coordinates, but I thought it might be useful to show a simple example where you can see how the math works.

Forget meshes for a minute. Lets create 4 locators. The first 3, I'll name loc0, loc1, loc2 and the 4th is just loc. I'll do this in an orthographic view (front, side or top) so that my locators are all in one plane. Then I move loc0, loc1, loc2 around a bit to define a triangle and I move loc so it is somewhere inside that triangle. Next, I select loc0, loc1, loc2 and loc in that order and create a pointConstraint with maintainOffset=False. Since each of the 3 constraint targets have the same weight, loc moves to the "barycenter" of the triangle (think center of mass).

But what if I dont want loc to move? What values do I need to set for each constraint target weight so that loc remains in its original position? The answer is, as you have probably guessed, the barycentric coordinates.

If you google "barycentric coordinates" you can read some detailed mathematical definitions, but I'm going to jump straight into a simple pymel example which is my own reworking of a post I read here on stackexchange. In this picture the point of interest, P, lies inside a triangle defined by the points P0, P1 and P2. The barycentric coordinates u, v and w are related to the area of each of the 3 segments of the subdivided triangle, each shown here in a different color. The formula shows that the position of P is simply the sum of the each of the triangle points multiplied by its corresponding barycentric coordinate element.

So if we relate this back to our constrained locator example, we need to compute the barycentric coordinates of loc relative to loc0, loc1 and loc2, then plug them into the constraint weights.

```def barycentricCoordinates(P0, P1, P2, P):
'''
Args
P0  (nt.Point)
P1  (nt.Point)
P2  (nt.Point)
P   (nt.Point)

Returns the barycentric coords of P
'''
v0 = P1 - P0
v1 = P2 - P0
v2 = P - P0

d00 = v0 * v0
d01 = v0 * v1
d11 = v1 * v1
d20 = v2 * v0
d21 = v2 * v1
denom = d00 * d11 - d01 * d01
v = (d11 * d20 - d01 * d21) / denom
w = (d00 * d21 - d01 * d20) / denom
u = 1.0 - v - w

return u, v, w
```

Then for my locator example...

```import pymel.core as pm

loc0 = pm.PyNode('loc0')
loc1 = pm.PyNode('loc1')
loc2 = pm.PyNode('loc2')
loc = pm.PyNode('loc')

P0 = loc0.getRotatePivot(space='world')
P1 = loc1.getRotatePivot(space='world')
P2 = loc2.getRotatePivot(space='world')
P = loc.getRotatePivot(space='world')

print barycentricCoordinates(P0, P1, P2, P)
```