Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting point on line segment that is closest to another point [closed]

Tags:

c++

math

sfml

I want to find a point on segment line AB, that is closest to another point P.

My idea was:

  1. Get a1 and b1 from line formula y1 = a1x + b1, using A and B points coordinates.
  2. Get normal line formula, from a1 and P coordinates, y2 = a2x + b2.
  3. Get intersection point x coord, by equating y1 and y2 and next using one of above formulas, to get y.

enter image description here

My code:

#include <SFML\Graphics.hpp>
#include <iostream>

sf::Vector2f getClosestPointOnLine(sf::Vector2f A, sf::Vector2f B, sf::Vector2f P)
{
    //convert to line formula
    float a = (B.y - A.y)/(B.x - A.x);
    float b = -a * A.x + A.y;

    //get normal line formula
    float a2 = -a / 2;
    float b2 = -a2 * P.x + P.y;

    //get x
    float a3 = a - a2;
    float b3 = b2 - b;

    float x = b3 / a3;

    //get y
    float y = a * x + b;

    return { x, y };
}

int main()
{
    sf::RenderWindow gameWindow(sf::VideoMode(800, 600), "App");

    sf::View view(sf::FloatRect(0, 0, 800, 600));
    gameWindow.setView(view);

    gameWindow.setFramerateLimit(60);

    sf::VertexArray plane(sf::LinesStrip, 2);

    plane[0] = { { view.getSize().x * 0.5f, view.getSize().y * 0.8f } };
    plane[1] = { { view.getSize().x * 0.8f, view.getSize().y * 0.6f } };

    sf::CircleShape ball(10);

    ball.setOrigin(10, 10);
    ball.setPosition({view.getSize().x * 0.7f, view.getSize().y * 0.4f});

    while (gameWindow.isOpen())
    {
        sf::Event e;
        while (gameWindow.pollEvent(e))
        {
            if (e.type == sf::Event::Closed)
            {
                gameWindow.close();
            }
        }

        //draw
        gameWindow.clear(sf::Color{30, 30, 30});

        ball.setPosition((sf::Vector2f)sf::Mouse::getPosition(gameWindow));

        sf::Vector2f closest = getClosestPointOnLine(plane[0].position, plane[1].position, ball.getPosition());

        sf::CircleShape cs(5);
        cs.setOrigin(5, 5 );
        cs.setPosition(closest);

        gameWindow.draw(cs);
        gameWindow.draw(plane);
        gameWindow.draw(ball);
        gameWindow.display();
    }
}

Result: enter image description here

As you can see function getClosestPointOnLine returns me wrong intersection point. What is wrong with my function?

------------------EDIT: As n.m. mentioned, -a / 2 is not formula for normal line slope, I was wrong with this formula, the right is: -1 / a.

like image 324
TKK Avatar asked Dec 10 '22 09:12

TKK


1 Answers

What you want is the projection of P onto the line segment. You can do this with the dot product:

auto AB = B - A;
auto AP = P - A;
float lengthSqrAB = AB.x * AB.x + AB.y * AB.y;
float t = (AP.x * AB.x + AP.y * AB.y) / lengthSqrAB;

Now, t is the interpolation parameter between A and B. If it is 0, then the point projects onto A. If it is 1, it projects onto B. Fractional values represent points in between. If you want to restrict the projection to the line segment, then you need to clamp t:

if(t < 0)
    t = 0;
if(t > 1)
    t = 1;

And finally, you can calculate the point:

return A + t * AB;
like image 65
Nico Schertler Avatar answered Feb 24 '23 16:02

Nico Schertler