Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Calculate point B of the Pythagoras right-angled triangle (IMAGE)

Discussion in 'General Graphics' started by Quatum1000, Jun 2, 2023.

  1. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    888
    Hi everyone,

    I have the following situation:
    Untitled-3.jpg

    I have:
    + Point A, already (hypotenuses)
    + P3, the player position Player.X, .Y (also the direction between A and Player.X, .Y)
    + P1 to P2 the Line (adjacent)
    + The length between point B and C must be the radius of the circle..
    + And we have the normals of the Line already.

    How to I calculate Point B, if the the length of the opposite is exactly the radius?
    Thank you..

    _____________________________________________________________________

    This gives me the correct CENTER point of the line.
    Code (CSharp):
    1. public double CircleLineSwept(ref PlayerStruc p, ref Point p1, ref Point p2, ref double normalX, ref double normalY)
    2. {
    3.     double dirX = p2.x - p1.x;
    4.     double dirY = p2.y - p1.y;
    5.     double length = Math.Sqrt(dirX * dirX + dirY * dirY);
    6.     dirX /= length;
    7.     dirY /= length;
    8.  
    9.     double x1 = p1.x;
    10.     double y1 = p1.y;
    11.     double x2 = p2.x;
    12.     double y2 = p2.y;
    13.     double x3 = p.x;
    14.     double y3 = p.y;
    15.     double x4 = p.x + p.dirX;
    16.     double y4 = p.y + p.dirY;
    17.  
    18.     double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
    19.     if (denom == 0)
    20.     {
    21.         return 1.0;
    22.     }
    23.     double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
    24.     double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
    25.     if (t >= 0 && t <= 1 && u >= 0)
    26.     {
    27.         double intersectionX = x1 + t * (x2 - x1);
    28.         double intersectionY = y1 + t * (y2 - y1);
    29.         normalX = intersectionX - p.x;
    30.         normalY = intersectionY - p.y;
    31.         length = Math.Sqrt(normalX * normalX + normalY * normalY);
    32.         normalX /= length;
    33.         normalY /= length;
    34.         return u;
    35.     }
    36.     return 1.0;
    37. }
     
  2. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    532
    First let's calculate the normalized vector from P1 to P2:
    P = P2 - P1 // tip - tail
    P /= sqrt(dot(P, P))

    Next let's calculate the normalized vector from A to P3:
    D = P3 - A
    D /= sqrt(dot(D, D))

    The dot product will give you the cosine of the angle between the two normalized vectors:
    cos(alpha) = dot(P, D) <->
    alpha = acos(dot(P, D))

    Now you have a triangle which three givens: The angle alpha, the opposite with length r (the radius) and the right angle. This means, you can calculate all the missing lengths and angles. We are interested in the hypotenuse c (length between A and B) which is calculated like this
    sin(alpha) = r / c <->
    c = r / sin(alpha)

    I think, this can be simplified because sin(acos(x)) = sqrt(1 - x * x):
    tmp = dot(P, D)
    c = r / sqrt(1 - tmp * tmp)

    Given the length c, it's now easy to calculate the center B:
    B = A + c * D

    Disclaimer: I haven't actually checked any of this
     
    Last edited: Jun 2, 2023
  3. UhOhItsMoving

    UhOhItsMoving

    Joined:
    May 25, 2022
    Posts:
    67
    You can solve this by getting the current radius of p3 to the line and scaling that to the desired radius.

    Shadertoy (GLSL) code:
    Code (CSharp):
    1. float sdSegment( in vec2 p, in vec2 a, in vec2 b ) // from: https://iquilezles.org/articles/distfunctions2d/
    2. { vec2 pa = p-a, ba = b-a; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ); }
    3.  
    4. void mainImage( out vec4 fragColor, in vec2 fragCoord )
    5. {
    6.     // Centered uv from -1 to 1
    7.     vec2 uv = vec2((fragCoord.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * fragCoord.y / iResolution.y - 1.);
    8.     vec2 muv = vec2((iMouse.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * iMouse.y / iResolution.y - 1.);
    9.     float uvscale = 10.; uv *= uvscale, muv *= uvscale; // scale uv
    10.  
    11.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    12.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    13.  
    14.     // Points
    15.     vec2 p1 = vec2(-12.37, -7.65), p2 = vec2(15.64, 4.07); // line points
    16.     vec2 p3 = vec2(2.8, 8.73); // player point
    17.     vec2 A = p1 + 0.3 * (p2 - p1); // A
    18.  
    19.     // Project p3 onto line p1p2
    20.     float t = dot(p3 - p1, normalize(p2 - p1)); // time of p3 on line p1p2
    21.     vec2 D = p1 + t * normalize(p2 - p1); // projected point
    22.  
    23.     // Radius (input)
    24.     float radius = iMouse.x < 1.5 ? 5. : 10. * iMouse.x / iResolution.x; // arbitrary value chosen by user
    25.     radius = clamp(radius, 0., distance(p3, D)); // clamps radius to range of p3 to A; not necessary
    26.  
    27.     // Get scale by dividing the desired radius by the distance of p3 to the projected point (i.e. the "current" radius)
    28.     float scale = radius / distance(p3, D);
    29.  
    30.     // Scale line *AD* (A to the projected point) by that value; this gives point C
    31.     vec2 C = A + scale * (D - A);
    32.  
    33.     // Point B (output) is C offset by the normal of the line (normalized vector of D to p3) multiplied by the desired radius
    34.     vec2 B = C + radius * normalize(p3 - D);
    35.  
    36.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    37.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    38.  
    39.     // Pixel color
    40.     vec3 col = vec3(1.);
    41.  
    42.     // Draw lines
    43.     if (sdSegment(uv, p1, p2) < 0.01 * uvscale) col = vec3(0.);
    44.     if (sdSegment(uv, p3, p3 + 1.3 * (A - p3)) < 0.01 * uvscale) col = vec3(1., 0., 0.);
    45.     if (sdSegment(uv, C, B) < 0.01 * uvscale) col = vec3(0., 1., 0.);
    46.  
    47.     // Draw circle (proof)
    48.     if (abs(distance(uv, B) - radius) < 0.01 * uvscale) col = vec3(0., 0., 1.);
    49.  
    50.     // Draw points
    51.     //if (distance(uv, p1) < 0.03 * uvscale) col = vec3(0., 0., 0.);
    52.     //if (distance(uv, p2) < 0.03 * uvscale) col = vec3(0., 0., 0.);
    53.     //if (distance(uv, p3) < 0.03 * uvscale) col = vec3(1., 0., 0.);
    54.     if (distance(uv, A) < 0.03 * uvscale) col = vec3(1., 0., 0.);
    55.     //if (distance(uv, D) < 0.03 * uvscale) col = vec3(1., 0., 0.);
    56.     if (distance(uv, C) < 0.03 * uvscale) col = vec3(0., 1., 0.);
    57.     if (distance(uv, B) < 0.03 * uvscale) col = vec3(0., 0., 1.);
    58.  
    59.     // Output to screen
    60.     fragColor = vec4(col, 1.);
    61. }
    upload_2023-6-4_14-10-12.png
    If you don't know what to do with this, just paste it into https://www.shadertoy.com/new to view & play with it. Moving the mouse left & right will change the radius.

    For the code itself, you only need what's in the commented bars. It's in GLSL, not C#, but the important part is the math, so it should be easy to convert. It should also work in 3D.

    For those who would like to know, here's the logic behind it:
    If we project a point we already know onto line p1p2 (e.g. p3), we will get a projected point on the line with a distance to the original point. If we treat this distance as a radius, then we now have an undesired point (p3) at an undesired radius (distance of p3 to the projected point). So, all we have to do is convert that undesired radius to a desired one. We can do this by scaling the projected point along line p1p2 such that the radius (i.e. the distance from point C to line p3A) is equal to our desired one. Point B is then obtained by simply offsetting point C by the line's "normal" (normalized vector of the projected point to p3) scaled by the desired radius.
     
    Last edited: Jun 4, 2023
  4. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    532
    Nice @UhOhItsMoving ! You used a completely different approach.

    I was curious to see whether my math worked as well so I took the liberty to plug it into your Shadertoy code (it does).

    Code (CSharp):
    1. float sdSegment( in vec2 p, in vec2 a, in vec2 b ) // from: https://iquilezles.org/articles/distfunctions2d/
    2. { vec2 pa = p-a, ba = b-a; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ); }
    3.  
    4. void mainImage( out vec4 fragColor, in vec2 fragCoord )
    5. {
    6.     // Centered uv from -1 to 1
    7.     vec2 uv = vec2((fragCoord.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * fragCoord.y / iResolution.y - 1.);
    8.     vec2 muv = vec2((iMouse.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * iMouse.y / iResolution.y - 1.);
    9.     float uvscale = 10.; uv *= uvscale, muv *= uvscale; // scale uv
    10.  
    11.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    12.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    13.  
    14.     // Points
    15.     vec2 p1 = vec2(-12.37, -7.65), p2 = vec2(15.64, 4.07); // line points
    16.     vec2 p3 = vec2(2.8, 8.73); // player point
    17.     vec2 A = p1 + 0.3 * (p2 - p1); // A
    18.  
    19.     // Radius (input)
    20.     float radius = iMouse.x < 1.5 ? 5. : 10. * iMouse.x / iResolution.x; // arbitrary value chosen by user
    21.  
    22.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    23.     // BEGIN c0d3_m0nk3y
    24.  
    25.     // First let's calculate the normalized vector from P1 to P2:
    26.     vec2 P = normalize(p2 - p1);
    27.  
    28.     // Next let's calculate the normalized vector from A to P3:
    29.     vec2 D = normalize(p3 - A);
    30.  
    31.     // Calculate length of hypotenuse (distance from A to B)
    32.     float tmp = dot(P, D);
    33.     float c = radius / sqrt(1.0 - tmp * tmp);
    34.  
    35.     // Given the length c, it's now easy to calculate the center B:
    36.     vec2 B = A + c * D;
    37.  
    38.     // END c0d3_m0nk3y
    39.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    40.  
    41.     // Calculate C only for visualization
    42.     vec2 C = B + radius * vec2(P.y, -P.x);
    43.  
    44.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    45.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    46.  
    47.     // Pixel color
    48.     vec3 col = vec3(1.);
    49.  
    50.     // Draw lines
    51.     if (sdSegment(uv, p1, p2) < 0.01 * uvscale) col = vec3(0.);
    52.     if (sdSegment(uv, p3, p3 + 1.3 * (A - p3)) < 0.01 * uvscale) col = vec3(1., 0., 0.);
    53.     if (sdSegment(uv, C, B) < 0.01 * uvscale) col = vec3(0., 1., 0.);
    54.  
    55.     // Draw circle (proof)
    56.     if (abs(distance(uv, B) - radius) < 0.01 * uvscale) col = vec3(0., 0., 1.);
    57.  
    58.     // Draw points
    59.     if (distance(uv, A) < 0.03 * uvscale) col = vec3(1., 0., 0.);
    60.     if (distance(uv, C) < 0.03 * uvscale) col = vec3(0., 1., 0.);
    61.     if (distance(uv, B) < 0.03 * uvscale) col = vec3(0., 0., 1.);
    62.  
    63.     // Output to screen
    64.     fragColor = vec4(col, 1.);
    65. }
    66.  
     
  5. Quatum1000

    Quatum1000

    Joined:
    Oct 5, 2014
    Posts:
    888
    Thank you!.. with this ideas I have extend the approach to a CircleLineCollision function. The player walks exactly along the right edge. But the code unfortunately extents the left point of the collider-line like : PLeft -= seglen. :confused: So it go wrong at the left point. The right point works perfekt! I need double precision calculations... this is so amazing fast.

    The code utilizes an offset line, which is calculated based on the player's collider size. This offset line is parallel to the original line segment and serves as an extended boundary for collision detection with the player's circular collider.
    It checks if the player's movement vector intersects with the offset line within the segment boundaries. If there is an intersection and the distance between the intersection point and the left collider point is within the segment length, it determines that a collision has occurred and returns the intersection point as the collision point.
    If the player is coming from below the line segment, indicating no collision, it returns the player's next position without any adjustments. Overall, the code handles collision detection and response for a player moving in a 2D space with line obstacles, utilizing an offset line concept to accurately detect and handle collisions.


    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public struct PointLineD
    5. {
    6.     public double x1;
    7.     public double y1;
    8.     public double x2;
    9.     public double y2;
    10. }
    11.  
    12. public struct PointD
    13. {
    14.     public double x;
    15.     public double y;
    16. }
    17.  
    18. public struct PlayerStruct
    19. {
    20.     public double x;
    21.     public double y;
    22.     public double dirx;
    23.     public double diry;
    24.     public double colliderSize;
    25.     public int NearestLine;
    26. }
    27.  
    28. public Vector2 CircleLineCollision(ref PlayerStruct player, ref PointD p1, ref PointD p2)
    29. {
    30.     double ix, iy, nx, ny, dx, dy, d, a, b, t;
    31.     Vector2 result = Vector2.zero;
    32.     PointLineD pl = new PointLineD();
    33.  
    34.     // Calculate the offset line
    35.     double segLen, ox, oy;
    36.     dx = p2.x - p1.x;
    37.     dy = p2.y - p1.y;
    38.     segLen = Mathf.Sqrt((float)(dx * dx + dy * dy));
    39.     ox = dy * player.colliderSize / segLen;
    40.     oy = -dx * player.colliderSize / segLen;
    41.     pl.x1 = p1.x + ox;
    42.     pl.y1 = p1.y + oy;
    43.     pl.x2 = p2.x + ox;
    44.     pl.y2 = p2.y + oy;
    45.  
    46.     // Calculate the normals of the offset line
    47.     nx = pl.y2 - pl.y1;
    48.     ny = pl.x1 - pl.x2;
    49.  
    50.     // Calculate the difference between the player's position and the first point of the offset line
    51.     dx = player.x - pl.x1;
    52.     dy = player.y - pl.y1;
    53.  
    54.     // Project the player vector onto the normals of the offset line
    55.     d = nx * dx + ny * dy;
    56.  
    57.     // Case: Player is coming from below
    58.     if (d < 0.0)
    59.     {
    60.         result.x = (float)(player.x + player.dirx);
    61.         result.y = (float)(player.y + player.diry);
    62.         return result;
    63.     }
    64.  
    65.     a = (pl.y2 - pl.y1) * player.dirx - (pl.x2 - pl.x1) * player.diry;
    66.     if (a != 0.0)
    67.     {
    68.         b = (pl.x1 - player.x) * (pl.y2 - pl.y1) - (pl.y1 - player.y) * (pl.x2 - pl.x1);
    69.         t = b / a;
    70.         if (t >= 0.0 && t <= 1.0)
    71.         {
    72.             ix = player.x + t * player.dirx;
    73.             iy = player.y + t * player.diry;
    74.  
    75.             dx = ix - pl.x1;
    76.             dy = iy - pl.y1;
    77.  
    78.             // !!! **************************************************************************************
    79.             // !!! here is the error!!!! why the heck is the left collider point position wrong only..
    80.             // !!! **************************************************************************************
    81.             if (Mathf.Sqrt((float)(dx * dx + dy * dy)) <= segLen)
    82.             {
    83.                 result.x = (float)ix;
    84.                 result.y = (float)iy;
    85.                 return result;
    86.             }
    87.         }
    88.     }
    89.  
    90.     // Case: Player passes by the offset line
    91.     result.x = (float)(player.x + player.dirx);
    92.     result.y = (float)(player.y + player.diry);
    93.     return result;
    94. }
    95.  
    96.  
    I know it's difficultly to find out what math cause the problem here.. but perhaps you have an idea..
    thanks a lot
     
  6. UhOhItsMoving

    UhOhItsMoving

    Joined:
    May 25, 2022
    Posts:
    67
    Are you extending the left point to the left and the right point to the right (e.g. similar to scaling the line from the center)? Because I think you're offsetting both points to the same side rather than extending them to both sides.