我有一个可以保存任何直线序列和/或立方贝塞尔曲线的QPainterPath
。现在,我有一个QPoint
,我需要计算QPainterPath
和点之间的最短距离。由于路径本身除了存储元素以将它们添加到路径中之外并没有做更多的事情,因此它本身不提供此类功能。我唯一的想法是使用 QPainterPath::toFillPolygon()
构造一个多边形,但这有时会返回一个等于路径的多边形,有时返回一个空多边形。而且,QPolygonF
对象只是一个点列表,其中一些是用线连接的,有些是用原始路径连接的,但我无法找出哪些是连接的,哪些不是。
是否有任何(简单的)解决方案来计算QPainterPath
(最好不转换为多边形)和QPoint
之间的最短距离?
QPainterPath
pointAtPercent()
,因此您可以在给定步骤迭代路径并检查路径上的多个点与目标点之间的距离。
这将为您提供大致最短的距离,如果您想要更高的精度,您可以专注于路径的这些部分并以更精细的步骤进行迭代。
将此代码片段粘贴到您的utility.cpp
或geom_tools.cpp
中。 它应该是一个通用工具,所以不应该进入一个自定义的QGraphicsItem子类。
#include <QVector2D>
#include <limits>
QPointF closestPointOnPath(const QPointF &point, const QPainterPath &path)
{
if (path.isEmpty())
return point;
auto vec = QVector2D(point);
auto poly = path.toFillPolygon();
float d, minDist = FLT_MAX;
QVector2D p, q, v, u, minVec;
for (int k=0; k < poly.count() - 1; k++)
{
p = QVector2D(poly.at(k));
if (k == poly.count() - 1)
k = -1;
q = QVector2D(poly.at(k+1));
v = q - p;
u = v.normalized();
d = QVector2D::dotProduct(u, vec - p);
if (d < 0.0f) {
d = (vec - p).lengthSquared();
if (d < minDist)
{
minDist = d;
minVec = p;
}
}
else if (d*d > v.lengthSquared())
{
d = (vec - q).lengthSquared();
if (d < minDist)
{
minDist = d;
minVec = q;
}
}
else {
u *= d;
u += p;
d = (vec - u).lengthSquared();
if (d < minDist)
{
minDist = d;
minVec = u;
}
}
}
if (minDist >= FLT_MAX)
return point;
return minVec.toPointF();
}
这会导致一个非常流畅的操作,例如,如果必须将箭头附加到节点,然后拖动箭头的另一端。 它将适用于圆角节点角等。 你给它传递一个QGrpahicsItem的shape(),它在项目的本地坐标中,所以point
也必须首先在项目的本地坐标中,或者你必须把它映射到那里(mapToItem, mapFromParent, mapFromScene
等)。
蟒:
def closest_point_on_path(point:QPointF, path:QPainterPath) -> QPointF:
if path.isEmpty():
return point
vec = QVector2D(point)
poly = path.toFillPolygon()
minDist = sys.float_info.max
for k in range(poly.count()):
p = QVector2D(poly.at(k))
if k == poly.count() - 1:
k = -1
q = QVector2D(poly.at(k+1))
v = q - p
u = v.normalized()
d = QVector2D.dotProduct(u, vec - p)
if d < 0.0:
d = (vec - p).lengthSquared()
if d < minDist:
minDist = d
minVec = p
elif d*d > v.lengthSquared():
d = (vec - q).lengthSquared()
if d < minDist:
minDist = d
minVec = q
else:
u *= d
u += p
d = (vec - u).lengthSquared()
if d < minDist:
minDist = d
minVec = u
if minDist >= sys.float_info.max:
return point
return minVec.toPointF()