Search code examples
pythonnumpylinear-algebraweighted-average

Find the weights from an existing point to position a point relative to a triangle using weighted average


My understanding is I can position a point anywhere inside a triangle in 3D space using a weighted average:

import numpy as np
triangle = np.arange(9)
triangle.shape = (3,3)
weights = np.array([1, 0.3, 20])
point = np.average(triangle, weights=weights, axis=0)

Now I have a point positioned in a triangle using weighted average. My question is how to do the inverse. If I have a triangle and I already have a point positioned inside it, can I get the weights from the relationship between the point and triangle that would let me use the weighted average to reposition the point? For example if the triangle moved and I want the point to move with it staying in the same position relative to the triangle. I realize this can be done with a barycentric calculation. I'm hoping there is a simpler way with weighted average.


Solution

  • Assuming the points are stored in the rows of triangle, and the points don't all lie in the same line, you can use:

    weights = np.linalg.solve(triangle.T, point)
    

    Here's an example. I'll use a modified version your triangle so the points are not in a line:

    In [57]: triangle
    Out[57]: 
    array([[9, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
    

    Using the same weights, compute point as the weighted average of the rows of triangle:

    In [58]: weights
    Out[58]: array([ 1. ,  0.3, 20. ])
    
    In [59]: point = np.average(triangle, weights=weights, axis=0)
    
    In [60]: point
    Out[60]: array([6.09859155, 6.67605634, 7.67605634])
    

    Now reverse the process: given point and triangle, find the weights:

    In [61]: w = np.linalg.solve(triangle.T, point)
    
    In [62]: w
    Out[62]: array([0.04694836, 0.01408451, 0.93896714])
    

    Note that w is normalized so its sum is 1. If we normalize weights, we see that it matches w:

    In [63]: weights / weights.sum()
    Out[63]: array([0.04694836, 0.01408451, 0.93896714])