2
\$\begingroup\$

I'm trying to get the relative point position on a quad using a [0,1] Vector2, like in the picture below:

Quads and Positions

The red dots are relative positions.

What I'm trying to make is a function that takes the A,B,C,D vertices, the relative position and return the local position of point.

Example using the first quad of the image:

// Returns Vector2(7, 7)
GetPositionOnQuad(Vector2(0.7f, 0.7f), Vector2(0,10), Vector2(10,10), Vector2(10,0), Vector2(0,0))

What are some ways of doing this?

\$\endgroup\$
1

2 Answers 2

4
\$\begingroup\$

Assuming that your quad is convex, you can solve for an interior point using a few linear interpolations (lerps) between the corner points of the quad.

To illustrate, consider finding a point (R for result) at (0.75, 0.5) within the interior of a quad with points A,B,C,D ordered as given in your description. Intuitively, we know that the result is going to be somewhat to the right of the middle. How can we find that?

First consider the simpler case of what would happen if we were looking for (0.75, 0.0). In that situation we would want the point (let's call it P) that sits 75% between A & B. We can find P by lerping between A & B. Similarly, if we simplified to the other extreme of (0.75, 1.0), we would find a point (let's call it Q) that's 75% between C & D. Much like before, we can find Q by lerping between C & D.

But we didn't actually want either extreme, we wanted something between them, something that accounts for the second parameter as given. Again, we can find that by lerping between the intermediate points P & R. In this case we wanted to be 50% between them.

Here's a general illustration:

diagram of quad & interior points

In terms of code, you might have something like this:

static float Lerp(float a, float b, float t)
{
    return a + t * (b - a);
}

static PointF Lerp(PointF a, PointF b, float t)
{
    return new PointF(Lerp(a.X, b.X, t), Lerp(a.Y, b.Y, t));
}

static PointF PointInQuad(PointF t, PointF a, PointF b, PointF c, PointF d)
{
    PointF p = Lerp(a, b, t.X);
    PointF q = Lerp(c, d, t.X);
    return Lerp(p, q, t.Y);
}
\$\endgroup\$
5
  • \$\begingroup\$ Thanks, worked like a charm! I implemented it in Godot C# by using Vector2.LinearInterpolate(b, t) built in function. \$\endgroup\$ Commented May 21, 2022 at 20:21
  • \$\begingroup\$ @PauloViníciusBettio Good call using the built in function. Using library code makes sense if it's available; I wasn't sure what you were using & didn't want to make assumptions. Good luck with your project! \$\endgroup\$
    – Pikalek
    Commented May 21, 2022 at 20:42
  • \$\begingroup\$ @Pikalek This is awesome! Thanks for explaining so easily. Would it be hard to adapt it to 3d (a relative point inside a cuboid)? I can't wrap my head around the math. \$\endgroup\$
    – st_phan
    Commented Dec 30, 2023 at 22:13
  • \$\begingroup\$ @st_phan Glad it was useful! Yes, the process can be generalized to 3d: basically you use the above to find points on opposing quads & then use interpolation yet again to find the final point between them. If you need more than that on it, consider posting question & I or someone else can provide a more detailed answer. \$\endgroup\$
    – Pikalek
    Commented Dec 31, 2023 at 21:37
  • \$\begingroup\$ @Pikalek Thanks, I think I understand. So you'd have R1 (on the top quad) and R2 (on the bottom quad) and lerp that again to have the final R somewhere in-between in space. \$\endgroup\$
    – st_phan
    Commented Jan 2 at 9:38
1
\$\begingroup\$

Find the points on the four sides according to the proportion, and then find the intersection of the two segments.

enter image description here

Code:

class Vec2:
    def __init__(self, x, y) -> None:
        self.x = x
        self.y = y

    def __add__(self, v):
        return Vec2(self.x+v.x, self.y+v.y)

    def __sub__(self, v):
        return Vec2(self.x-v.x, self.y-v.y)

    def __mul__(self, n):
        return Vec2(self.x*n, self.y*n)

def GetPositionOnQuad(pos,A,B,C,D):
    p1 = A+(B-A)*pos.x
    p2 = D+(C-D)*pos.x
    p3 = A+(D-A)*pos.y
    p4 = B+(C-B)*pos.y
    return GetIntersection(p1,p2,p3,p4)

def det(a, b):
    return a.x * b.y - a.y * b.x

def GetIntersection(p1,p2,p3,p4):
    xdiff = Vec2(p1.x - p2.x, p3.x - p4.x)
    ydiff = Vec2(p1.y - p2.y, p3.y - p4.y)

    div = det(xdiff, ydiff)
    if div == 0:
        return -1,-1

    d = Vec2(det(p1,p2), det(p3,p4))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y

result = GetPositionOnQuad(Vec2(0.7,0.7),Vec2(0,0),Vec2(0,10),Vec2(10,10),Vec2(10,0))
print(result)
\$\endgroup\$
3
  • 1
    \$\begingroup\$ It looks like you're answering "given a point in the 0-1 square, how can I find the corresponding point in the arbitrary quad?" but to my reading, OP is asking for the opposite: given a point in an arbitrary quad, how can they find its relative proportional coordinates in the 0-1 square? Your method is also more complicated than it needs to be: you can do this with three lerps, no line intersections needed. \$\endgroup\$
    – DMGregory
    Commented May 20, 2022 at 11:58
  • \$\begingroup\$ @DMGregory I had already started to work up a 3 lerp style answer, but reading your comment about the directionality has me second guessing inputs & outputs as my read seems to mirror the logic of the above answer. How does Mangata's code not match OP's sample I/O? \$\endgroup\$
    – Pikalek
    Commented May 20, 2022 at 14:48
  • \$\begingroup\$ I'll grant that I could be misreading it. I find the phrasing of the post unclear in how it defines "relative position" vs "local position". \$\endgroup\$
    – DMGregory
    Commented May 20, 2022 at 15:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .