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