我使用Qt 5可视化不同种类的几何形状。
我有一个QRect
可视化为填充或未填充。
现在我想使用QPoint
计算boost::geometry
到该矩形的距离。
矩形内的一个点在填充时应该有0
,在未填充时应该到下一行的距离。
由于Box
的文档没有提到它是一个形状,我认为我可以在这种情况下使用它并将Box
概念改编为QRect
。
以下示例不起作用,因为Box
被视为形状,因此始终“填充”。
#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
的手动修改,否则这将是相当有用的。
我如何才能最好地解决这个问题?
答案 0 :(得分:1)
确实你需要右线 - 因为Box意味着填充的形状。实际上,在我的快速测试中,多边形也是如此。
你当然可以创造一个假的&#34;多孔的&#34;边缘宽度较小的多边形。但那是作弊,当然效率不高
的确,你可以在这里使用线串:
<强> 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
我没有理由相信线串的效率低于多边形(它基本上与多边形的外环相同)。
然而,实际上盒子测试可能会更快。我建议你介绍一下。如果速度更快,只需使用盒子,以防已知形状为“#34;填充&#34;否则就是一个线串。
答案 1 :(得分:1)
支持非填充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()
返回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());
}