将此代码段粘贴到您的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
等)。
Python:
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()