Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Circular interpolation instead of linear interpolation

Discussion in 'Scripting' started by newlife, Jun 24, 2019.

  1. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    Hello, i have two points A and B that stay on a known circumference (I know its radius).
    Now Im making a linear weighted interpolation in this way:

    M = (a*A + b*B)/(a+b)

    where a and b are the weights.
    I would like to make a circular interpolation, that is M should always be a point of that circumference.
    Which is the fastest way to do this?

    circular_interpolation.png
     
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
  3. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    Pretty sure for Vector3.Slerp(A,B,T), A and B are interpreted as directions from the center of the circle, not world positions. Return value should too be a direction relative from the center as well.
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    The two points A and B do not, by themselves, uniquely define a circle. (There are infinite possible circles that intersect both points.) So in the general case, you need enough extra information to determine which circle you want to interpolate along.

    One possible way to do that is to provide the center of the circle; call it C. Then I believe C + Slerp(A-C, B-C) should give you the answer you want. (Notice that if C is the origin, this is the same as just Slerp(A, B).)

    There might be faster ways to do it taking advantage of the fact that you know A-C and B-C have equal magnitude, but I couldn't list them off-hand. Unless you are doing this calculation thousands of times per frame, you probably won't notice any efficiency difference.
     
    rtorpey22 likes this.
  5. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    Thank you for the replies.
    In order to use Vector3.Slerp, I have to normalize the weights (a and b) cause T is [0,1].
    Im sorry for the silly question, but how to do this? I cant find a solution...
     
  6. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    T = b / (a+b)
     
    Last edited: Jun 25, 2019
    newlife likes this.
  7. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Mostly correct. They are interpreted as positions in the local space of the circle. Both the local direction and length of the vectors are interpolated, so the final output is a local space position, not a direction. In this case, you need to convert the inputs to local space before the slerp, and the output to world space after the slerp. I'll explain below.


    You can normalize a vector by just accessing it's ".normalized" value. You can also get a normalized vector by dividing the vector by it's length (which you know, since it's the circle radius). However, I don't think you're heading in the right direction with that train of thought.


    Slerp isn't suitable for world-space positions, you'll want to move the positions into the local space of your circle by subtracting the position of the center of the circle from them.

    Code (CSharp):
    1. Vector3 localA = A - center;
    2. Vector3 localB = B - center;


    Now you Slerp between those 2 values to get your new value, which is in the local space of the circle.

    Code (CSharp):
    1. Vector3 localC = Vector3.Slerp(localA, localB, 0.5f);


    Finally, you can convert your new position on the circle to world space by adding the center position of the circle to the local position of the new point.

    Code (CSharp):
    1. Vector3 worldC = localC + center;
     
    newlife likes this.
  8. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I don't believe newlife is talking about normalizing the vectors, but about normalizing the weights used for interpolation.

    Like if you say "I want a mixture of these two things, where the first one is given twice as much weight as the second" then you have interpolation weights of 2 and 1 respectively.
     
  9. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Oh I see... if the weights are giving a value outside of 0 to 1, I don't think Slerp is the right option. Slerp will only give a position on the circle between the 2 input positions.

    Maybe it would be a better idea to convert those vectors to angles, multiply the angles by their weights, combine them, then convert the output angle back to a vector.
     
  10. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    It's just a different way of expressing values in the same range.

    Slerp wants a single number that expresses how far along you are from one point to the other.

    A "weighted average" will give a value in the allowed range, you just need to convert it. As I said above, weights A and B give you a value T = B / (B+A), which will always be in the range [0, 1] provided that A and B are nonnegative.
     
  11. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    Yes this works! To sum up:
    Code (CSharp):
    1. M = C + Vector3.Slerp(A - C, B - C, b/(a + b))
    Unfortunately, this is not enough cause my case in actually 3D, not 2D. So i have several contact points instead of one single point A and several contact points instead of one single point B.
    Ideally, I should have to make the centroid of the points in A, then the centroid of the points in B and finally the circular interpolation between the two centroid.
    Unfortunately, this is not straightforward cause its not easy / possible to manage two sets of contact points in a 3D collision in Unity.
    Anyway, I have to investigate better, cause all points in A and B shares the same normal.
     
    Last edited: Jun 26, 2019
  12. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I don't believe I made any assumptions regarding the number of dimensions in my solution. I'm not sure what you mean by "contact points".

    But calculating centroids is easy (it's just the average of all the points in the set), so if you want to interpolate between the centroids of two collections, that sounds straightforward.
     
  13. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    Unity also has LerpUnclamped and SlerpUnclamped.

    It's funny, since before Unity existed everyone know Lerp and Slerp can go past either end. But I think going 110% of the way from A to B confused game designers, so _they_ liked the normal version being clamped.
     
  14. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    Yes but the unclamped versions are useful when you need to go over the limits (A and B), which is not the case.
     
  15. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    In order to make things more clear, I made an image showing whats is going on:

    contactpoints.png

    As you can see, there are two set of contact points managed via onCollisionStay. Im using a custom contact point manager in order to have more control on these points (taken from https://forum.unity.com/threads/det...s-with-the-same-collider.252031/#post-1665689) modified in order to make the centroid of all contact points. Its working, but as for the question of this thread, its basically a linear interpolation of ALL the contact points, while I would like to obtain a circular interpolation between the two centroids.

    Hope that now is more clear.
     
  16. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    Unlike Unity Answers, forum threads are expected to wander. Some people wanted to talk about weighted averages and outside-bounds lerps, which is fine. Of course, it's also fine for you to try to get people to come back to your problem. To me, it sounds like settling on a solution too quickly. I often want to make something "go in the correct direction", quickly pick math, solve it, and when it gives odd results, realize "the correct direction" wasn't what I assumed it was.
     
  17. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    Ok so it seems that I found a decent solution.
    Basically Im calculating the Sin / Cos of the angle between the first normal and the interpolated normal and Im adding this value to the interpolated point (centroid), in the direction of the interpolated normal.

    Code (CSharp):
    1.  
    2. float angle = Vector3.Angle(contatcs[0].normal, interpolatedNormal);
    3. if (angle<45) centroid = centroid - interpolatedNormal*Mathf.Sin(angle*Mathf.Deg2Rad)*0.5f;
    4. else centroid = centroid - interpolatedNormal*Mathf.Cos(angle*Mathf.Deg2Rad)*0.5f;
    circular_interpolation2.png

    The interpolated normal is represented by dotted yellow lines (interpolated between the two normals in A and in B), the centroid is represented by the points a1, a2, a3 (interpolated between the two points A and B).
    contatcs[0].normal
    is the normal of the first contact point (in this case A).
    Let me know what you think about this solution.
     
  18. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    I have extended the previous code in order to make it work for any position of A and B in the circle:

    Code (CSharp):
    1. angleNormal1 = Vector3.Angle(contatcs[0].normal, interpolatedNormal);
    2. angleNormal2 = Vector3.Angle(contatcs[0].normal, contatcs[contatcs.Count - 1].normal);
    3. angle = (angleNormal1/angleNormal2)*90;
    4. if (angle<45) centroid = centroid - interpolatedNormal*Mathf.Sin(angle*Mathf.Deg2Rad)*0.5f;
    5. else centroid = centroid - interpolatedNormal*Mathf.Cos(angle*Mathf.Deg2Rad)*0.5f;

    contatcs[0].normal
    is the normal in point A.


    contatcs[contatcs.Count - 1].normal
    is the normal in point B.

    Unfortunately, it doesn't seem to work in all cases.
     
  19. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
    May I ask what is it that you want to use this for? is it like to chop a part of the circle, make an object go throw it's curve when it collides, or what? I think knowing more about the context may help understand what are the limitations and challenges.
     
    Last edited: Jun 27, 2019
  20. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    I need this to find the correct contact points for a rgidbody wheel Im using for my vehicle simulation. So the circle in actually is a 3D cylinder, so when there are two different contact area, and I should make the centroid of the first set of contact points, then the centroid of the second area and the a circular interpolation between the two centroids.
    Right now Im making a centroid of ALL the contact points (so actually a linear interpolation between all the points), now Im trying to convert the result to a circular interpolation.
    The easiest way to solve this would be to calculate two different centroid for the two different set of contact points, but in Unity is not easy to manage separately these two sets.

    contactpoints2.png
     
  21. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
    Ok, let me see if I understood:
    So, you have a wheel, with a collider. The wheel may collide with 2 objects so to speak: one beign the floor on the bottom and the other beign an object like say a wall, a person, etc.
    So, let's say that your wheel hits a wall.
    Both surfaces overlap for a frame, and the contact points are the points were the overlapping is happening. I take you're retriving that data using something like Collision.GetContacts.
    Now, you want to find the average between them (the "centroid" of all contact points) to know what's the center of the impact or something, right? Like the epicenter of the impact.
    Am I undestanding it up to this point? Is this your objective?
    To get the average position (centroid) of a bunch of contact points you take them and lerp them. But if I understood you, getting a a linear interpolation is not accurate enough for what you need, since the wheel's surface is not flat.
    Did I understand that part?
    Now, since the wheel is (circular? ... not really) you want to get a circular interpolation instead of a linear one. This is when I start loosing your reasoning: I'm guessing the reason you want a circular interpolation is because for some reason you're removing one dimension out of the equation. You're thinking of a side view of the wheel and thinking it as a circle. For some reason you don't care to calculate the X component of your centroid, you just care for the side view position. Maybe you can figure the x position with another method
    Is this correct up to this point?
    So, for just a second, say that I want to find that mid point between the contact points like a caveman. Imagine for the sake of the argument, that I were to place an empty game object parented to the wheel with zero local transformations, is a clone transform of the wheel's one. Let's call this the pivot. Now, imagine I'd put a child to this object, and place it at the radius distance. Now, you could rotate the pivot and the child would always follow the circumference of my wheel. Imagine I now place the child in the same position as the first contact point and find the angle. Then I place the second child at the second contact position and find the angle.
    Of course, you don't really need to parent anything or create any objects, you can just check the angles between the center of the wheel and the contact points (you'd have to zero the X axis to get your 2D circle view of the cylinder).
    Now I have 2 angles, each from one contact point.
    The average of both is inside the space were one is but the other one isn't. so imagine I subtract the smallest angle. After all, were they overlap, the centroid can't be. Now I have like an ideal section of the distance between both points. Of this angle between both I want to find the center, so I divide the result in 2. Now, I have the center, if I add I the smallest angle that I had subracted earlier I get the angle the centroid has.
    Once you know the angle of the centroid and you know the radius of the circle, you can find the Z & Y using trigonometry. You know, soh-cah-toa. Your local Z has the same value as the adjacent side of the triangle. And you local Z has the same value as the opposite side of the tirangle.
    Is this is the problem you want to solve?
     
    Last edited: Jun 30, 2019
  22. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    I think that I finally found a solution for this.
    I'll try to make things more clear: each collision is composed by a set of contact points, normals and impulses.
    For my needs, I can use only one contact point per collision, so my idea is to calculate the centroid of the sets of contact points and then make a circular interpolation between the centroids (for my needs I dont need more than two different set of collisions so Im ok with Mathf.Slerp).
    Its not easy to implement this in unity cause there is no explicit way to have control of two different collisions. I had to use a custom contact point structure, and use two different custom contact point variables (one for each collision).
    In this way I can make the centroid of each collision and then I can make the circular interpolation between the two points.
    Thanks all for the help.
     
    Last edited: Jul 1, 2019
  23. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
  24. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,081
    @calpolican thank you for your detailed description! Your proposal is indeed interesting, but has a small limitation: its not always true the fact that the centroid of the collision is on the center of the wheel (respect of x axis), so making the centroid of the contact points led to more precise results.
     
  25. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
    There are no limitations, I just thought it wasn't worth an explanation. All you have to add, is:
    1) Copy the impact positions at the beginning.
    2) Follow the method I've shown to know the Y& Z positions of your centroid.
    3) Grub the original contact positions and set the Y & Z axis to zero.
    4) Find the midle point of the 2 points. You can use a lerp or just divide the distance (a wheel has no curvature in X).
    5) Add the X component to the Z & Y you had.
     
    Last edited: Jul 1, 2019
  26. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
    PS: While I'm positive that what I said above should work for what you want to find, I just wanted to point out that I wouldn't use this method to find the result of several collisions.
    If indeed Unity can't handle more than one collision, what I would try to do is find all the active forces.You can probably do this finding the speed of the rigidbody of each colliding object.You'd really also need to calculate the mass, as force = mass * acceleration.
    What you'd get are force vectors were the direction represent the direction of movement, and the magnitud represents the force.
    From this I'd add up all the foreces and I'd add my object movng force too. This should tell you in what direction your object should move as a reaction.
    How you're doing it you really don't know how strong it should be pushed towards the resultant direction, and there are many cases that are probably not covered.
    Collisions for a car are very difficult because you have to calculate how they'll affect the entire mass of the car, like that the wheel will want to rotate towards its wheel axis, and on the transversal axis there's a hell of friction. But at least adding all of the impacting forces will give you the basic result, with a physical perspective, and probably better than the way you're looking for now.
    Anyway, just wanted to say that.
     
    Last edited: Jul 1, 2019