I want to find a point on segment line AB, that is closest to another point P.
My idea was:
a1 and b1 from line formula y1 = a1x + b1, using A and B points coordinates.a1 and P coordinates, y2 = a2x + b2.y1 and y2 and next using one of above formulas, to get y.
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:

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.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With