有没有办法在QT中通过一组点绘制平滑线? 在运行时设置点的数量和位置。
目前,我绘制了一个QPainterPath,它包含了从一点到另一点的lineTo,创建了一条路径。我确实使用渲染提示抗锯齿,但路径仍然是锯齿状的。
我见过QSplineSeries似乎提供了这种曲线路径,但Qt4.8中没有它,这是QT版本I使用的。
经常建议的另一个选项是使用Bezier曲线,但是那些使用一个起点和终点以及两个控制点,所以我需要为每个段(每个行)计算它,并以某种方式计算我不喜欢的那些控制点现在没有。
答案 0 :(得分:1)
最后,我实现了某种解决方法,它基本上采用了两条连线,删除了它们之间的连接点,并用曲线替换它。由于我有很多小线条,这些变化不可见,我删除所有非常短的线并重新连接开放端。该功能主要由Bojan Kverh提供,查看他的教程:https://www.toptal.com/c-plus-plus/rounded-corners-bezier-curves-qpainter
这里的功能:
namespace
{
float distance(const QPointF& pt1, const QPointF& pt2)
{
float hd = (pt1.x() - pt2.x()) * (pt1.x() - pt2.x());
float vd = (pt1.y() - pt2.y()) * (pt1.y() - pt2.y());
return std::sqrt(hd + vd);
}
QPointF getLineStart(const QPointF& pt1, const QPointF& pt2)
{
QPointF pt;
float rat = 10.0 / distance(pt1, pt2);
if (rat > 0.5) {
rat = 0.5;
}
pt.setX((1.0 - rat) * pt1.x() + rat * pt2.x());
pt.setY((1.0 - rat) * pt1.y() + rat * pt2.y());
return pt;
}
QPointF getLineEnd(const QPointF& pt1, const QPointF& pt2)
{
QPointF pt;
float rat = 10.0 / distance(pt1, pt2);
if (rat > 0.5) {
rat = 0.5;
}
pt.setX(rat * pt1.x() + (1.0 - rat)*pt2.x());
pt.setY(rat * pt1.y() + (1.0 - rat)*pt2.y());
return pt;
}
}
void PainterPath::smoothOut(const float& factor)
{
QList<QPointF> points;
QPointF p;
for (int i = 0; i < mPath->elementCount() - 1; i++) {
p = QPointF(mPath->elementAt(i).x, mPath->elementAt(i).y);
// Except for first and last points, check what the distance between two
// points is and if its less then min, don't add them to the list.
if (points.count() > 1 && (i < mPath->elementCount() - 2) && (distance(points.last(), p) < factor)) {
continue;
}
points.append(p);
}
// Don't proceed if we only have 3 or less points.
if (points.count() < 3) {
return;
}
QPointF pt1;
QPointF pt2;
QPainterPath* path = new QPainterPath();
for (int i = 0; i < points.count() - 1; i++) {
pt1 = getLineStart(points[i], points[i + 1]);
if (i == 0) {
path->moveTo(pt1);
} else {
path->quadTo(points[i], pt1);
}
pt2 = getLineEnd(points[i], points[i + 1]);
path->lineTo(pt2);
}
delete mPath;
mPath = path;
prepareGeometryChange();
}
答案 1 :(得分:0)
我认为Qt 4.8中没有开箱即用的解决方案(因为您注意到QSplineSeries
是Qt 5.x功能)。另外QSplineSeries
是QtCharts
模块的一部分,它是商业模块(如QtDataVisualization
),因此,除非您拥有商业许可或您的项目是GPL,否则您无法使用它。
你必须手动完成它所需的数学运算并自己实现(或找到一个很好的实现(不需要在C ++中,更不用说与Qt兼容)。)
既然你已经提到了Bezier曲线,我建议给composite Bezier curve一个镜头。我记得为我所从事的项目实现了那个东西。它需要一些......工作。 :D This article可能会帮助您入门。
Bezier曲线实际上是B样条(如果我没记错的话)。特别是如果你能够在一定程度上缺乏平滑度的情况下,你可以非常快速地生成复合贝塞尔曲线。为了他们的健壮性和受欢迎程度,我百分百肯定你可以在网上找到一个体面的实施。可能不是Qt友好的,但如果写得正确,你应该能够立即调整代码。
This看起来非常有前途(它在ActionScript中,但是meh)。或者您可以给QPainterPath::cubicTo()
一个可以为您创建贝塞尔曲线的镜头,因为您还可以提供计算曲线所需的两个控制点。
答案 2 :(得分:0)
几乎每个人都使用三次插值来完成此任务,您可以选择Bezier曲线或Catmull-Rom样条曲线。如果你必须击中每一点,那么你需要保持&#34;手柄&#34;或Beziers控制点之间的直线。然后你使用最小二乘拟合,你已经发现它有点涉及。
Catmull Rom样条曲线的优势在于它们只需要两个额外的控制点(开始和结束,只需要镜像点来创建它们)。只要点数相当平滑,线条就会表现得很好。 QT图形不太可能直接绘制CatMull Rom样条,因此转换为Beziers,这是一种标准的发布方法,你可以很容易地从Catmull Rom转到Bezier,尽管不是相反 - 不是每个Bezier都可以由Catmull Rom代表只有几点。
你可以使用其他插值方法,例如quintic,如果Cubics不能给你你想要的曲线。