如何计算点 o filled/unfilled 矩形的距离

How to calculate distance from point o filled/unfilled rectangle

我正在使用 Qt 5 可视化不同种类的几何图形。

我有一个 QRect 可视化为已填充或未填充。 现在我想使用 boost::geometry 计算 QPoint 到该矩形的距离。 矩形内的点在填充时应有 0 的距离,在未填充时应与下一行的距离。

由于 Box 的文档没有提到它是一种形状,我认为我可以将它用于这种情况,并将 Box 概念改编为 QRect.

但以下示例不起作用,因为 Box 被视为形状,因此总是 "filled"。

#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <QtCore/QPoint>
#include <QtCore/QRect>

BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPoint, int, boost::geometry::cs::cartesian, x, y, setX, setY);

namespace boost { namespace geometry {

namespace traits
{
    template <> struct tag<QRect>        { typedef box_tag type; };
    template <> struct point_type<QRect> { typedef QPoint type; };

    template <std::size_t Index, std::size_t Dimension>
    struct indexed_access<QRect, Index, Dimension>
    {
        typedef typename geometry::coordinate_type<QRect>::type coordinate_type;

        static inline coordinate_type get(const QRect &r)
        {
            if (Index == boost::geometry::min_corner)
                return geometry::get<Dimension>(r.topLeft());
            else
                return geometry::get<Dimension>(r.bottomRight());
        }
    };
}
}}

double distance(const QPoint &p, const QRect &r, const bool filled)
{
    if (filled && r.contains(p))
        return 0.0;
    else
        return boost::geometry::distance(p, r);
}

int main()
{
    QRect r(QPoint(0, 0), QPoint(20, 10));
    QPoint p(5, 5); // whithin rect

    // 0, instead of 5
    std::cout << "not filled: " << distance(p, r, false) << '\n';

    // 0, as expected
    std::cout << "filled: " << distance(p, r, true) << '\n';
}

运行 g++ -Wall -O2 -fPIC main.cpp -I/usr/include/qt -lQtCore 在 Linux.

上构建

我当然可以将 LineString 用于未填充的情况,尽管那样会有动态分配。 除非我创建一个使用底层 QRect 的手动改编,否则这将是一项相当大的工作。

如何最好地解决这个问题?

的确,您是对的,因为 Box 表示填充形状,所以需要 line-string。实际上,在我的快速测试中,多边形也是如此。

You could of course create a fake "holey" polygon that has an edge of some small width. But that's cheating and certainly less efficient

的确,你可以在这里使用线串:

Live On Coliru

#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>

using namespace boost::geometry;

int main()
{
    using Point = model::d2::point_xy<double>;
    using Rect  = model::linestring<Point>;

    Rect rect;
    rect.insert(rect.end(), {
            Point {  0,  0 },
            Point { 10,  0 },
            Point { 10, 20 },
            Point {  0, 20 },
            Point {  0,  0 },
        });

    std::cout << "distance point within: "     << distance(rect, Point(5, 5))  << '\n'; // 0
    std::cout << "distance point not within: " << distance(rect, Point(15, 5)) << '\n'; // 5
}

打印

distance point within: 5
distance point not within: 5

我看不出有任何理由相信线串比多边形效率低(它基本上与多边形的外环相同)。

不过,确实盒测可能会更快。我建议你分析一下。如果速度更快,只需使用盒子,以防形状已知为 "filled",否则使用线串。

支持非填充 QRect 的一种相对简单的方法是使用 LineString 概念。 为了避免分配的开销,可以使用 std::array

在初始代码的基础上,需要添加以下部分:

#include <array>

using RectLineString = std::array<QPoint, 5>;
BOOST_GEOMETRY_REGISTER_LINESTRING(RectLineString)

double distance(const QPoint &p, const QRect &r, const bool filled)
{
    if (filled && r.contains(p))
        return 0.0;
    else
    {
        RectLineString rls;
        fillRectLineString(rls, rect);
        return boost::geometry::distance(p, rls);
    }
}

fillrectLineString 应该是什么样子取决于您希望如何处理 QRect::bottomRight() returns QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1) 的问题。 所以我在这里提供两个版本:

// bottomRight() is QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1)
void fillRectLineString1(RectLineString &rls, const QRect &rect)
{
    rls[0] = rect.topLeft();
    rls[1] = rect.topRight();
    rls[2] = rect.bottomRight();
    rls[3] = rect.bottomLeft();
    rls[4] = rect.topLeft();
}

// bottomRight() is QPoint(rect.x() + rect.width(), rect.y() + rect.height())
void fillRectLineString2(RectLineString &rls, const QRect &rect)
{
    rls[0] = QPoint(rect.x(), rect.y());
    rls[1] = QPoint(rect.x() + rect.width(), rect.y());
    rls[2] = QPoint(rect.x() + rect.width(), rect.y() + rect.height());
    rls[3] = QPoint(rect.x(), rect.y() + rect.height());
    rls[4] = QPoint(rect.x(), rect.y());
}