Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question [Solved] InverseLerp of Vector2, all normalized ?

Discussion in 'Scripting' started by msh91, May 17, 2024.

  1. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    I'm sure this should be an easy math question, but I've been fighting this for hours and can't figure it out; tried googling and AI too.

    How can I find out the inverse lerp of Vector2? That is:

    IF:
    Code (CSharp):
    1. // A is normalized
    2. // B is normalized
    3. // A and B are NOT colinear
    4. Vector2 N = Vector2.Lerp(A, B, pct)
    And we have A, B, and pct, we need to implement the method `InverseLerp2` so that:
    float pct = InverseLerp2(A, B, N);



    To clarify, I don't need the *exact* pct that was used - but rather any pct that will make the following true:
    Code (CSharp):
    1. N == Vector2.Lerp(A, B, InverseLerp2(A, B, N)) // assume an equal is done using floating point estimation/threshold, not an exact equal
     
    Last edited: May 18, 2024
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,551
    The code formatting here is not `markdown`. See the code-tag.png buttons.

    For any type,
    h = Lerp(hmin, hmax, InverseLerp(xmin, xmax, x))
    should be equivalent to:

     ( ((x)-(xmin)) * ((hmax)-(hmin)) / ((xmax)-(xmin)) + (hmin) )


    Note this formula does not even use a
    t
    as an intermediate value.

    If you just want the
    t
    along a line, you can figure out which axis is longer (x or y) and then do the Mathf.InverseLerp along that axis. For example,

    Code (csharp):
    1. if (Mathf.Abs(A.x - B.x) > Mathf.Abs(A.y - B.y))
    2.     t = Mathf.InverseLerp(A.x, B.x, N);
    3. else
    4.     t = Mathf.InverseLerp(A.y, B.y, N);
    The reason you want to do this on the longer axis is just to avoid the division by zero if the shorter axis is actually zero. Avoid it altogether if A and B are the same point.
     
    Last edited: May 17, 2024
    samana1407 and Bunny83 like this.
  3. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    @halley thanks for the response!

    1. I edited the message to use code blocks rather than markdown.
    2. it still doesn't work for me, not sure what I missed though:

    A. You mentioned the formula does not even use t, although I'm guessing it's because you inlined the InverseLerp method call as part of the math formula - since that is t.
    B. In your code example the InverseLerp is using N but that's a Vector, so I used N.y and N.y respectively; not sure if that is what you meant. Result code:

    Code (CSharp):
    1.         private float InverseLerp(Vector2 A, Vector2 B, Vector2 N)
    2.         {
    3.             float t;
    4.             if (Mathf.Abs(A.x - B.x) > Mathf.Abs(A.y - B.y))
    5.                 t = Mathf.InverseLerp(A.x, B.x, N.x);
    6.             else
    7.                 t = Mathf.InverseLerp(A.y, B.y, N.y);
    8.  
    9.             // Test t:
    10.             Vector2 result = Vector2.Lerp(A, B, t);
    11.             Vector2 resultNormalized = result.normalized;
    12.  
    13.             // Debug and check if resultNormalized is equal to N
    14.  
    15.             // Return t
    16.             return t;
    17.         }
    The values that I tested with:
    Code (CSharp):
    1. +        result    "(0.71, -0.42)"    UnityEngine.Vector2
    2. +        resultNormalized    "(0.86, -0.51)"    UnityEngine.Vector2
    3. +        A    "(0.91, -0.42)"    UnityEngine.Vector2
    4. +        B    "(-0.91, -0.42)"    UnityEngine.Vector2
    5. +        N    "(0.71, -0.71)"    UnityEngine.Vector2
    6.  
    What am I missing?
     
  4. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    819
    The value N.y (-0.71) is outside the range of A.y (-0.42) & B.y (-0.42), so it's going to be clamped by the InverseLerp function, right?
     
  5. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    @flashframe I guess, but it's only if you think of it as individual values.

    Attaching an example screenshot, not matching the values but just an example:

    upload_2024-5-18_14-31-2.png

    Note: Imagine they're all in the same length, I was just using paint xd

    The point I'm trying to pass is that Y might not be in range, but it's the direction that matter and should eventually lead to it.
     

    Attached Files:

  6. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    819
    I might be completely misunderstanding, so apologies if I am, but using your test values, this is how I imagined the plot would look:

    Untitled.jpg

    N doesn't lie on the linear interpolated line AB
     
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,189
    You said that N is a vector that is the result of
    Code (CSharp):
    1. Vector2 N = Vector2.Lerp(A, B, pct)
    However in the example you have given, that is NOT the case. You have normalized your vector N. So N is no longer on the linear line between A and B. So your premise you have given isn't true to start with.

    You wouldn't / shouldn't use lerp with direction vectors anyways as you would not get a smooth interpolation between A and B when you normalize the result. So this doesn't work properly in general. You usually "slerp" direction vectors, so they stay on the same arc and keep it's length. There's no inverseSlerp implementation I know of. For Vector2 it wouldn't be that difficult as we can simply calculate the angles between the vectors and work with the angles instead. I guess that's what you want to do here.

    Unfortunately Unity does not provide a Slerp for Vector2 for some reason. Though we could use the Vector3 version instead.

    Code (CSharp):
    1.  
    2. // slerp N based on p. If A and B are normalized, N should also be normalized
    3. Vector2 N = Vector3.Slerp(A, B, p);
    4.  
    5. // now do the inverse
    6. float ab = Vector2.Angle(A, B);
    7. float an = Vector2.Angle(A, N);
    8. float t = an / ab;
    9.  
    10. //Now "t" and "p" should be about the same
    11.  
     
  8. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,319
    Now would be a good time to elaborate on what you're trying to accomplish. You may be trying to solve the wrong problem.
     
    orionsyndrome and Bunny83 like this.
  9. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    Sorry for the long delay - got a full time job and was sick too :(

    @Bunny83 I think the confusion lies in there are "2 versions" of N; I'll explain the entire goal, and my way of thinking, so hopefully it will be clear.

    ------------------

    Imagine there's a board, and a ball that goes towards it.
    There are 2 "methods"/calculations in here:
    1. When the ball hit the board, the game/ball will calculate how it should be reflected on it. Looking at the screenshot, the board will be the black line. A and B will be lines that are defined by the game designer as the extent to where the ball can be reflected to. Depending on where on the board it will be hit, the reflected line will be based upon.
    1.a. If the ball hit the right-most end of the board - it will be reflected as vector A.
    1.b. If the ball hit the left-most end of the board - it will be reflected as vector B.
    1.c. Anything in between should be linear interpolated. So N would be the reflected vector of the ball, if it was hit EXACTLY at the middle of the board (assuming A and B are exactly orthogonal, although I prefer not to restrict it, but possible if needed)
    2. The 2nd part - in some parts I want to calculate the board position, in order for the ball to be reflected at a specific direction. That is, if we know the ball is coming at some vector P, and I can calculate where it will hit the board (the board is only moving horizontal) - which so far seems to be done correctly, I then need to calculate at which position the board should be in order for the ball to be reflected correctly.
    2.a. in other words, if I have A and B, and I know the logic for (1), what t (which is a number from 0..1, on the board) needs to be in order for the ball end result (as defined by 1) to be M?

     
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,213
    1st part
    you want to reflect
    and you want to normalize a so-called midpoint vector
    Code (csharp):
    1. var mid = (.5f * (a + b)).normalized;
    although lerp would also work in the generalized case
    Code (csharp):
    1. var mid = lerp(a, b, .5f).normalized;
    you need to normalize to maintain its length of 1, which it loses because the end tip is on a straight line between a and b.
    if you imagine a and b being collinear or opposite vectors, then the lerp is invalid, because you'll get a zero vector.

    so the above works only when
    abs(dot(a, b)) < 1

    it's also called nlerp (for normalized lerp), it's sometimes used instead of slerp (spherical lerp)

    2nd part
    I can't understand a thing, it's late and it's complicated

    here's inverse interpolation in 2D
    Code (csharp):
    1. static public float invLerp(Vector2 a, Vector2 b, Vector2 v) {
    2.   int i = imax(abs(b - a)); // find the "best" dimension
    3.   return invLerp(a[i], b[i], v[i]); // compute inverse lerp (t)
    4. }
    where
    Code (csharp):
    1. static public float invLerp(float a, float b, float v) => substNaN((v - a) / (b - a), 1f);
    2. static public int imax(Vector2 v) => v.x < v.y? 1 : 0;
    3. static public Vector2 abs(Vector2 v) => new(Math.Abs(v.x), Math.Abs(v.y));
    4. static public float substNaN(float n, float s) => float.IsNaN(n)? s : n;
     
  11. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    @orionsyndrome I don't really want to reflect, no.
    Reflection would be more realistic, but this is a game - and not really following realistic case.
    upload_2024-6-1_1-12-39.png
    Imagine A and B are still the same vectors (just positioned at different positions), and C is vector of the ball.
    The result vector should be A, since it hit the right-most position on the board (black line). A is not a reflection of C on anything though.

    I am aware the above lerp only works while A and B are non-colinear/opposite though.
    I can even tell you they'll always be in the same "half square"; e.g. in the picture above, both A and B will have Y < 0.

    Note: I'm using horizontal black line as an example, but it can be vertical too though. A and B will then look different too though.


    2nd part - I tried to use that code, it still didn't work. I'm assuming I still use Vector2.lerp
    let me know if there's anything I can add for you guys to help me with it, and thanks for the attempts so far!
     

    Attached Files:

  12. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,189
    So you want to recreate the classical Breakout game? Where the ball is unrealistically bouncing off the paddle based on where the ball hit the paddle? If that's your goal, I'm not sure why you want to inverse lerp between your two direction vectors. You usually just calculate the relative position of the ball to the paddle center and use the paddle with to "normalize" the position on the paddle. Based on that position you simply choose the rebounce direction the ball should bounce off This can be done with a simple Slerp between the two extreme normals. Since you actually need a t value between 0 and 1, it probably make more sense to convert the ball position into a local space that has it's origin at the left edge of the paddle. That way dividing by the paddle width it directly gives you the "t" value to choose your direction.

    About your second part: The best solution here is to work with angles as I've shown in post #7.

    If that's NOT what you want to do, please be more specific, give examples (like this online game for example) or be more descriptive
     
  13. msh91

    msh91

    Joined:
    Apr 22, 2017
    Posts:
    44
    @Bunny83 it's similar to the breakout game, but it has different variations. Anyway I got it working! :D
    The solution was angles related, although yours didn't work.
    I then remember before posting this I tried to use an angle solution as well, I tried it - and it did work!
    Seems like I had to use slerp for my angle solution to work.

    What I did was:
    1. Calculate angleA which is the angle between "right" vector and "a"
    2. Calculate angleB which is the angle between "right" vector and "b"
    3. Calculate angleDesired which is the angle between "right" vector and the "desired" vector
    4. Return t from InverseLerp(angleA, angleB, angleDesired).

    This seems to work perfectly now.
    Thanks everyone for all the help!! :D