【问题标题】:Getting point on line segment that is closest to another point [closed]在最接近另一个点的线段上获取点[关闭]
【发布时间】:2018-05-08 23:17:35
【问题描述】:

我想在线段 AB 上找到一个点,它离另一个点 P 最近。

我的想法是:

  1. 使用 A 点和 B 点坐标从直线公式 y1 = a1x + b1 中获取 a1b1
  2. a1 和P 坐标y2 = a2x + b2 获取法线公式。
  3. 通过将y1y2 等同起来获得交点x 坐标,然后使用上述公式之一获得y。

我的代码:

#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();
    }
}

结果:

如您所见,函数 getClosestPointOnLine 返回错误的交点。 我的功能有什么问题?

------------------编辑: 作为 n.m.提到,-a / 2不是法线斜率的公式,这个公式我错了,正确的是:-1 / a

【问题讨论】:

  • 为了融合所有的实现细节,给出什么,你想要什么? (好图顺便说一句)
  • float a2 = -a / 2; 不是法线公式,你从哪里得到的?你的想法也没有考虑到线条可以是垂直的也可以是水平的。
  • @user1767754 我有奇怪的偏移,线段中大点和小点之间的线必须垂直于该线段。

标签: c++ math sfml


【解决方案1】:

您想要的是将P 投影到线段上。您可以使用点积来做到这一点:

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;

现在,tAB 之间的插值参数。如果是0,则该点投影到A。如果是1,则投影到B。小数值代表介于两者之间的点。如果要限制投影到线段,则需要钳位t

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

最后,你可以计算点:

return A + t * AB;

【讨论】:

  • 您能否准确解释一下 t 计算的工作原理和原因?
  • @BossCode:如果你对它背后的数学感兴趣,请查看Wikipedia article about vector projection。那里的数字比我在评论中更好地形象化了这个概念。
猜你喜欢
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 2018-04-21
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 2020-12-24
  • 2014-10-14
相关资源
最近更新 更多