17

I have a unit right triangle and a value at each of the 3 vertices. I need to interpolate to find the value at a point inside the triangle. Hours of searching have turned up nothing that actually tells me how to do this. Here is my closest attempt, which is actually pretty close but not quite right -

                result = 
                v1 * (1 - x) * (1 - y) +
                v2 * x * (1 - y) +
                v3 * x * y;

v1, v2, and v3 are the values at the 3 vertices of the triangle. (x, y) is the point in the triangle that you are trying to find the value of.

Any kind of method would help me here. It doesn't necessarily need to be a unit/right triangle.

Updated info: I have a grid of evenly spaced points and a value at each point. I make a triangle out of the nearest 3 points on the grid. Here is a picture to illustrate it - enter image description here
So I have to interpolate between 5, 3, and 7 to find the value of x. The point could also be inside the other triangle, meaning you would interpolate between 5, 7, and the value of the bottom left corner of the square.

In the code I showed, v1 = 5, v2 = 3, v3 = 7.
x is the fractional distance (range [0-1]) in the "x" direction, and y is the fractional distance in the "y" direction.
In the picture's example, x would probably be about 0.75 and y would be about 0.2

Here are my closest attempts -
enter image description here
Created using -

        if (x > y) //if x > y then the point is in the upper right triangle
            return
                v1 * (1 - x) * (1 - y) +
                v2 * x * (1 - y) +
                v3 * x * y;
        else //bottom left triangle
            return
                v1 * (1 - x) * (1 - y) +
                v4 * (1 - x) * y +
                v3 * x * y;

And another attempt -
enter image description here
Created using -

if (x > y)
            return
                (1 - x) * v1 + (x - y) * v2 + y * v3;
        else
            return
                (1 - y) * v1 + (y - x) * v4 + x * v3;

They're both close to what I need but obviously not quite right.

user1118321
  • 25,567
  • 4
  • 55
  • 86
Frobot
  • 1,224
  • 3
  • 16
  • 33
  • So which vertex is which? Show me how your coordinate system works, which way x and y go and where v1 v2 and v3 are. – Dan Jan 02 '12 at 05:01
  • @Dan Ok I updated some info to tell what I'm doing a bit more in depth. – Frobot Jan 02 '12 at 05:21
  • Do you have a specific interpretation method in mind? Linear/bilinear/nearest neighbor? – rsaxvc Jan 02 '12 at 05:47
  • I'm going for linear/bilinear. Nearest neighbor won't work. – Frobot Jan 02 '12 at 05:56
  • 1
    Does triangle actually matter here? Would a 4-point interpolation do? – rsaxvc Jan 02 '12 at 06:13
  • 1
    4 point bilinear interpolation is how I usually do it, but it's looking like I can greatly improve my speed by using triangles instead. I usually interpolate between v1 and v2, then v3 and v4, then interpolate again between these 2 new values for the final value. I'd like to do something similar but with a triangle. – Frobot Jan 02 '12 at 06:15
  • That would show artifacts - is this acceptable to you? – rsaxvc Jan 02 '12 at 06:32
  • artifacts are usually ok with this unless they're too large – Frobot Jan 02 '12 at 06:38
  • Considered splitting each square into four triangles by using a single 4-point interpolation to get the center value, then doing 4 triangles (top/left/right/bottom)? – rsaxvc Jan 07 '12 at 07:11
  • I've tried that and several other methods. I tried interpolating the triangle the way you would a simplex grid, and also tried using a radial type of interpolation. But each method has some sort of artifact that is a little too strong. – Frobot Jan 08 '12 at 01:28
  • Bicubic interpolation is your friend: https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.interp2d.html – Dan H Jul 05 '18 at 16:28
  • By the way, there is no "perfect" or "artifact free" way to interpolate. it is highly subjective, and will vary depending on the data set you are sampling and the noise (if any) in the measurements. – Dan H Jul 05 '18 at 16:29

5 Answers5

20

You should use barycentric coordinates. There is a very thorough write-up here that also discusses alternative solutions and why barycentric coordinates are best: CodePlea - Interpolating in a Triangle

Basically, the weights will end up looking like this:

enter image description here

zx485
  • 28,498
  • 28
  • 50
  • 59
reneekk
  • 223
  • 2
  • 4
7

Actually the simplest and most robust solution is based on barycentric coordinates -

http://answers.unity3d.com/questions/383804/calculate-uv-coordinates-of-3d-point-on-plane-of-m.html

titanae
  • 2,259
  • 2
  • 21
  • 31
2

I asked this 3 years ago and have still been working on a way to do this. I do believe it is impossible to do it without artifacts unless using an equilateral triangle. Here is a decent way to do it using barycentric coordinates and then adding a technique that gets rid of most of the artifacts. v1, v2, v3 are the values at the three points of the triangle. x, y is the point you want to find a value for.

if (x > y)
        {
            b1 = -(x - 1);
            b2 = (x - 1) - (y - 1);
            b3 = 1 - b1 - b2;
        }
        else
        {
            b1 = -(y - 1);
            b2 = -((x - 1) - (y - 1));
            b3 = 1 - b1 - b2;
        }

        float
            abs = x - y;
        if (abs < 0) abs *= -1;
        if (abs < 0.25f)
        {
            abs = 0.25f - abs;
            abs *= abs;
            b1 -= abs;
            b3 -= abs;
        }

        b1 *= b1;
        b2 *= b2;
        b3 *= b3;
        float fd = 1 / (b1 + b2 + b3);
        b1 *= fd;
        b2 *= fd;
        b3 *= fd;

        return
                v1 * b1 +
                v2 * b2 +
                v3 * b3;
Frobot
  • 1,224
  • 3
  • 16
  • 33
1

Ok, so we will do a linear interpolation, assuming that the gradient is constant with respect to x and to y. d/dx = v2 - v1 and d/dy = v3 - v2, and f(0,0) = v1. We have a simple two dimensional differential equation.

d{f(x,y)} = (v2 - v1)*dx
f(x,y) = (v2 - v1)*x + g(y)
d{f(x,y)} = g'(y) = (v3 - v2)*dy
g(y) = (v3 - v2)*y + C
f(x,y) = (v2 - v1)*x + (v3 - v2)*y + C
f(0,0) = v1 = (v2 - v1)*0 + (v3 - v2)*0 + C = C
f(x,y) = (v2 - v1)*x + (v3 - v2)*y + v1

or in terms of v1 v2 and v3

f(x,y) = (1 - x)*v1 + (x - y)*v2 + y*v3

If you want to do it in a square for four vertices, as above with v4 in the bottom left at x=0 y=1, here are the conditions: d/dx = (v2 - v1) (1 - y) + (v3 - v4) y, d/dy = (v3 - v2) x + (v4 - v1) (1 - x), f(0,0) = v1

d/dx = (v2 - v1) (1 - y) + (v3 - v4) y
f(x,y) = (v2 - v1) (1 - y) x + (v3 - v4) y x + g(y)
d/dy = (v3 - v2) x + (v4 - v1) (1 - x) = -(v2 - v1) x + (v3 - v4) x + g'(y)
v3 - v2 + (v4 - v1) / x + v4 - v1 = -v2 + v1 + v3 - v4 + g'(y) / x
(v4 - v1) / x + 2*(v4 - v1) = g'(y) / x
g'(y) = (v4 - v1) + 2 x (v4 - v1)
g(y) = (v4 - v1) (1 + 2 x) y + C
f(x,y) = (v2 - v1) (1 - y) x + (v3 - v4) y x + (v4 - v1) (1 + 2 x) y + C
f(0,0) = (v2 - v1) (1 - 0) 0 + (v3 - v4) 0 0 + (v4 - v1) (1 + 2 0) 0 + C = v1
f(x,y) = (v2 - v1) (1 - y) x + (v3 - v4) y x + (v4 - v1) (1 + 2 x) y + v1
Dan
  • 10,531
  • 2
  • 36
  • 55
  • So assuming this will get the right value, I also have to account for the points that won't be in the upper right triangle, and will instead be in the lower left part of the square. So I also need a v4. I tried this - `if (x > y) //if the point is in the upper right triangle return (1 - x) * v1 + (x - y) * v2 + y * v3; else //if the point is in the lower left triangle return (1 - x) * v1 + (x - y) * v4 + y * v3;` But it's not working – Frobot Jan 02 '12 at 06:06
  • What kind of gradient /is/ this that you're trying to generate? If you want to do it based on a square then it's a bit more complicated because `d/dx = (v2 - v1) (1 - y) + (v3 - v4) y` and `d/dy = (v3 - v2) x + (v4 - v1) (1 - x)`. It's still possible. – Dan Jan 02 '12 at 06:10
  • 1
    Not quite sure what you're asking, but it's a smoothed noise function I'm making. I have it working using the full square and doing bilinear interpolation, similar to perlin noise, but I'm trying to get it to work using triangles instead of squares. – Frobot Jan 02 '12 at 06:19
  • I found a very close method but the artifacts are pretty bad. [picture](http://i200.photobucket.com/albums/aa143/kronikel/b-1.jpg) – Frobot Jan 02 '12 at 07:00
0

Here is some pseudocode for nearest-neighbor:

if( dist( p, p1 ) <= dist( p, p2 ) && dist( p, p1 ) <= dist( p, p3 ) )
  return val( p1 )
if( dist( p, p2 ) <= dist( p, p3 ) && dist( p, p2 ) <= dist( p, p1 ) )
  return val( p2 )
if( dist( p, p3 ) <= dist( p, p1 ) && dist( p, p3 ) <= dist( p, p2 ) )
  return val( p3 )

I think this also generates a voronoi diagram

rsaxvc
  • 1,675
  • 13
  • 20
  • I need some form of linear interpolation because nearest neighbor will just produce big squares of color – Frobot Jan 02 '12 at 05:57
  • Not squares. It would only generate squares when using sampling points at square locations, and you specified triangles. – rsaxvc Jan 02 '12 at 06:09
  • Sorry I mean it will make big solid colored triangles* – Frobot Jan 02 '12 at 06:22
  • No, it would not. It will have big solid colored areas though, but not triangles - this would generate 4 sided shapes. For a point, it would make two sides being half the outer edges of the triangle, and two perpendicular bisectors of lines to the other points. – rsaxvc Jan 02 '12 at 06:35
  • Ah ok. I haven't tested it out, but the point is it won't fit the job. – Frobot Jan 02 '12 at 06:39