在QCustomPlot中,並無爲咱們提供平滑曲線,因此須要咱們改造它app
注意:改造須要修改源碼
注意:改造須要修改源碼
注意:改造須要修改源碼ide
2020-6-6日更新修復當數據中有NaN的數據時平滑曲線的顯示問題
來源:公孫二狗 ,在這裏感謝狗哥
生成平滑曲線的方法我也不知道,反正拿來用就是了函數
class SmoothCurveGenerator { protected: static QPainterPath generateSmoothCurveImp(const QVector<QPointF> &points) { QPainterPath path; int len = points.size(); if (len < 2) { return path; } QVector<QPointF> firstControlPoints; QVector<QPointF> secondControlPoints; calculateControlPoints(points, &firstControlPoints, &secondControlPoints); path.moveTo(points[0].x(), points[0].y()); // Using bezier curve to generate a smooth curve. for (int i = 0; i < len - 1; ++i) { path.cubicTo(firstControlPoints[i], secondControlPoints[i], points[i+1]); } return path; } public: static QPainterPath generateSmoothCurve(const QVector<QPointF> &points) { QPainterPath result; int segmentStart = 0; int i = 0; int pointSize = points.size(); while (i < pointSize) { if (qIsNaN(points.at(i).y()) || qIsNaN(points.at(i).x()) || qIsInf(points.at(i).y())) { QVector<QPointF> lineData(QVector<QPointF>(points.constBegin() + segmentStart, points.constBegin() + i - segmentStart)); result.addPath(generateSmoothCurveImp(lineData)); segmentStart = i + 1; } ++i; } QVector<QPointF> lineData(QVector<QPointF>(points.constBegin() + segmentStart, points.constEnd())); result.addPath(generateSmoothCurveImp(lineData)); return result; } static QPainterPath generateSmoothCurve(const QPainterPath &basePath, const QVector<QPointF> &points) { if (points.isEmpty()) return basePath; QPainterPath path = basePath; int len = points.size(); if (len == 1) { path.lineTo(points.at(0)); return path; } QVector<QPointF> firstControlPoints; QVector<QPointF> secondControlPoints; calculateControlPoints(points, &firstControlPoints, &secondControlPoints); path.lineTo(points.at(0)); for (int i = 0; i < len - 1; ++i) path.cubicTo(firstControlPoints[i], secondControlPoints[i], points[i+1]); return path; } static void calculateFirstControlPoints(double *&result, const double *rhs, int n) { result = new double[n]; double *tmp = new double[n]; double b = 2.0; result[0] = rhs[0] / b; // Decomposition and forward substitution. for (int i = 1; i < n; i++) { tmp[i] = 1 / b; b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; result[i] = (rhs[i] - result[i - 1]) / b; } for (int i = 1; i < n; i++) { result[n - i - 1] -= tmp[n - i] * result[n - i]; // Backsubstitution. } delete[] tmp; } static void calculateControlPoints(const QVector<QPointF> &knots, QVector<QPointF> *firstControlPoints, QVector<QPointF> *secondControlPoints) { int n = knots.size() - 1; firstControlPoints->reserve(n); secondControlPoints->reserve(n); for (int i = 0; i < n; ++i) { firstControlPoints->append(QPointF()); secondControlPoints->append(QPointF()); } if (n == 1) { // Special case: Bezier curve should be a straight line. // P1 = (2P0 + P3) / 3 (*firstControlPoints)[0].rx() = (2 * knots[0].x() + knots[1].x()) / 3; (*firstControlPoints)[0].ry() = (2 * knots[0].y() + knots[1].y()) / 3; // P2 = 2P1 – P0 (*secondControlPoints)[0].rx() = 2 * (*firstControlPoints)[0].x() - knots[0].x(); (*secondControlPoints)[0].ry() = 2 * (*firstControlPoints)[0].y() - knots[0].y(); return; } // Calculate first Bezier control points double *xs = nullptr; double *ys = nullptr; double *rhsx = new double[n]; // Right hand side vector double *rhsy = new double[n]; // Right hand side vector // Set right hand side values for (int i = 1; i < n - 1; ++i) { rhsx[i] = 4 * knots[i].x() + 2 * knots[i + 1].x(); rhsy[i] = 4 * knots[i].y() + 2 * knots[i + 1].y(); } rhsx[0] = knots[0].x() + 2 * knots[1].x(); rhsx[n - 1] = (8 * knots[n - 1].x() + knots[n].x()) / 2.0; rhsy[0] = knots[0].y() + 2 * knots[1].y(); rhsy[n - 1] = (8 * knots[n - 1].y() + knots[n].y()) / 2.0; // Calculate first control points coordinates calculateFirstControlPoints(xs, rhsx, n); calculateFirstControlPoints(ys, rhsy, n); // Fill output control points. for (int i = 0; i < n; ++i) { (*firstControlPoints)[i].rx() = xs[i]; (*firstControlPoints)[i].ry() = ys[i]; if (i < n - 1) { (*secondControlPoints)[i].rx() = 2 * knots[i + 1].x() - xs[i + 1]; (*secondControlPoints)[i].ry() = 2 * knots[i + 1].y() - ys[i + 1]; } else { (*secondControlPoints)[i].rx() = (knots[n].x() + xs[n - 1]) / 2; (*secondControlPoints)[i].ry() = (knots[n].y() + ys[n - 1]) / 2; } } delete xs; delete ys; delete[] rhsx; delete[] rhsy; } };
mSmooth
爲咱們添加的一個bool
型的類成員變量,而且咱們限制了QCPGraph
的線風格mLineStyle
爲lsLine
的時候纔會真正的繪製平滑曲線spa
void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const { if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0) { applyDefaultAntialiasingHint(painter); if (mSmooth && mLineStyle == lsLine) painter->drawPath(SmoothCurveGenerator::generateSmoothCurve(lines)); else drawPolyline(painter, lines); } }
這時候已經能夠實現平滑曲線了,來個簡單的例子看下debug
void MainWindow::setupSmoothCurveDemo(QCustomPlot *customPlot) { QVector<double> xdata = { 1, 2, 3, 4, 5, 6, 7 }; QVector<double> ydata = { 820, 932, 901, 934, 1290, 1330, 1320 }; QCPGraph *graph = customPlot->addGraph(); graph->setPen(QPen(Qt::red, 2)); graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QColor(Qt::red), QColor(Qt::white), 6)); graph->setData(xdata, ydata); graph->setSmooth(true); // 開啓平滑曲線 customPlot->xAxis->setRange(0, 8); customPlot->yAxis->setRange(0, 1500); }
鑑於不少人說不知道setSmooth函數是什麼,特意放上來的!!!
mSmooth是咱們本身新增的QCPGraph的一個bool型成員變量
mSmooth是咱們本身新增的QCPGraph的一個bool型成員變量
mSmooth是咱們本身新增的QCPGraph的一個bool型成員變量code
// QCPGraph.h頭文件 class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D<QCPGraphData> { public: void setSmooth(bool smooth); // 新增內容 protected: bool mSmooth; // 新增內容 }
// QCPGraph.cpp源文件 void QCPGraph::setSmooth(bool smooth) { mSmooth = smooth; }