zoukankan      html  css  js  c++  java
  • Qt 绘制平滑曲线

    本文介绍在 Qt 中绘制平滑曲线的实现,调用下面的函数 SmoothCurveGenerator::generateSmoothCurve(points) 即可。默认曲线的 2 个顶点之间被分割为 16 个小线段来拟合曲线,下图展示了 tension 为 0.5(默认值) 的曲线效果,tension 并不是越大越好,默认的 0.5 大多数时候就不错。

    算法来自于 How to draw smooth curve through N points using javascript HTML5 canvas?

    SmoothCurveGenerator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 文件名: SmoothCurveGenerator.h
    #ifndef SMOOTHCURVEGENERATOR_H
    #define SMOOTHCURVEGENERATOR_H
     
    #include <QList>
    #include <QPainterPath>
     
    class SmoothCurveGenerator {
    public:
    /**
    * @brief generateSmoothCurve 的重载函数
    */
    static QPainterPath generateSmoothCurve(QList<QPointF> points, bool closed = false, double tension = 0.5, int numberOfSegments = 16);
     
    /**
    * @brief 使用传入的曲线顶点坐标创建平滑曲线。
    *
    * @param points 曲线顶点坐标数组,
    * points[i+0] 是第 i 个点的 x 坐标,
    * points[i+1] 是第 i 个点的 y 坐标
    * @param closed 曲线是否封闭,默认不封闭
    * @param tension 密集程度,默认为 0.5
    * @param numberOfSegments 平滑曲线 2 个顶点间的线段数,默认为 16
    * @return 平滑曲线的 QPainterPath
    */
    static QPainterPath generateSmoothCurve(QList<double>points, bool closed = false, double tension = 0.5, int numberOfSegments = 16);
    };
     
    #endif // SMOOTHCURVEGENERATOR_H
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    // 文件名: SmoothCurveGenerator.cpp
    #include "SmoothCurveGenerator.h"
    #include <QtMath>
     
    QPainterPath SmoothCurveGenerator::generateSmoothCurve(QList<QPointF> points, bool closed, double tension, int numberOfSegments) {
    QList<double> ps;
     
    foreach (QPointF p, points) {
    ps << p.x() << p.y();
    }
     
    return SmoothCurveGenerator::generateSmoothCurve(ps, closed, tension, numberOfSegments);
    }
     
    QPainterPath SmoothCurveGenerator::generateSmoothCurve(QList<double> points, bool closed, double tension, int numberOfSegments) {
    QList<double> ps(points); // clone array so we don't change the original points
    QList<double> result; // generated smooth curve coordinates
    double x, y;
    double t1x, t2x, t1y, t2y;
    double c1, c2, c3, c4;
    double st;
     
    // The algorithm require a previous and next point to the actual point array.
    // Check if we will draw closed or open curve.
    // If closed, copy end points to beginning and first points to end
    // If open, duplicate first points to befinning, end points to end
    if (closed) {
    ps.prepend(points[points.length() - 1]);
    ps.prepend(points[points.length() - 2]);
    ps.prepend(points[points.length() - 1]);
    ps.prepend(points[points.length() - 2]);
    ps.append(points[0]);
    ps.append(points[1]);
    } else {
    ps.prepend(points[1]); // copy 1st point and insert at beginning
    ps.prepend(points[0]);
    ps.append(points[points.length() - 2]); // copy last point and append
    ps.append(points[points.length() - 1]);
    }
     
    // 1. loop goes through point array
    // 2. loop goes through each segment between the 2 points + 1e point before and after
    for (int i = 2; i < (ps.length() - 4); i += 2) {
    // calculate tension vectors
    t1x = (ps[i + 2] - ps[i - 2]) * tension;
    t2x = (ps[i + 4] - ps[i - 0]) * tension;
    t1y = (ps[i + 3] - ps[i - 1]) * tension;
    t2y = (ps[i + 5] - ps[i + 1]) * tension;
     
    for (int t = 0; t <= numberOfSegments; t++) {
    // calculate step
    st = (double)t / (double)numberOfSegments;
     
    // calculate cardinals
    c1 = 2 * qPow(st, 3) - 3 * qPow(st, 2) + 1;
    c2 = -2 * qPow(st, 3) + 3 * qPow(st, 2);
    c3 = qPow(st, 3) - 2 * qPow(st, 2) + st;
    c4 = qPow(st, 3) - qPow(st, 2);
     
    // calculate x and y cords with common control vectors
    x = c1 * ps[i] + c2 * ps[i + 2] + c3 * t1x + c4 * t2x;
    y = c1 * ps[i + 1] + c2 * ps[i + 3] + c3 * t1y + c4 * t2y;
     
    //store points in array
    result << x << y;
    }
    }
     
    // 使用的平滑曲线的坐标创建 QPainterPath
    QPainterPath path;
    path.moveTo(result[0], result[1]);
    for (int i = 2; i < result.length() - 2; i += 2) {
    path.lineTo(result[i+0], result[i+1]);
    }
     
    if (closed) {
    path.closeSubpath();
    }
     
    return path;
    }

    Form

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 文件名: Form.h
    #ifndef FORM_H
    #define FORM_H
     
    #include <QWidget>
    #include <QList>
    #include <QPainterPath>
     
    class Form : public QWidget {
    Q_OBJECT
     
    public:
    explicit Form(QWidget *parent = 0);
    ~Form();
     
    protected:
    void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
     
    private:
    QList<QPointF> points1; // 曲线一的顶点数组
    QList<QPointF> points2; // 曲线二的顶点数组
    QPainterPath smoothCurvePath1; // 平滑曲线一
    QPainterPath smoothCurvePath2; // 平滑曲线二
    };
     
    #endif // FORM_H
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    // 文件名: Form.cpp
    #include "Form.h"
    #include "SmoothCurveGenerator.h"
     
    #include <QDebug>
    #include <QPainter>
    #include <QDateTime>
     
    Form::Form(QWidget *parent) : QWidget(parent) {
    qsrand(QDateTime::currentDateTime().toTime_t());
     
    // 随机生成曲线第一条曲线的坐标
    int x = 0, y = 0;
    for (int i = 0; i < 100; ++i) {
    x += qrand() % 30 + 20;
    y = qrand() % 180 + 30;
     
    points1 << QPointF(x, y);
    }
     
    // 第二条星行曲线的坐标
    points2 << QPointF(0, 150) << QPointF(50, 50) << QPointF(150, 0) << QPointF(50, -50)
    << QPointF(0, -150) << QPointF(-50, -50) << QPointF(-150, 0) << QPointF(-50, 50);
     
    // 使用曲线的坐标生成平滑曲线
    smoothCurvePath1 = SmoothCurveGenerator::generateSmoothCurve(points1); // 第一条曲线不封闭
    smoothCurvePath2 = SmoothCurveGenerator::generateSmoothCurve(points2, true); // 第二条曲线是封闭的
    }
     
    Form::~Form() {
    }
     
    void Form::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(QPen(Qt::black, 2));
     
     
    // 绘制第一条平滑曲线和曲线上的顶点
    painter.drawPath(smoothCurvePath1);
    painter.setBrush(Qt::gray);
    for (int i = 0; i < points1.length() ; i += 1) {
    painter.drawEllipse(points1[i].x()-3, points1[i].y()-3, 6, 6);
    }
     
    // 绘制第二条平滑曲线和曲线上的顶点
    painter.translate(200, 400);
    painter.drawPath(smoothCurvePath2);
    for (int i = 0; i < points2.length() ; i += 1) {
    painter.drawEllipse(points2[i].x()-3, points2[i].y()-3, 6, 6);
    }
    }
     
     
    http://www.qtdebug.com/qt-smooth-curve-1/
  • 相关阅读:
    [DB] 数据库的连接
    JS leetcode 翻转字符串里的单词 题解分析
    JS leetcode 拥有最多糖果的孩子 题解分析,六一快乐。
    JS leetcode 搜索插入位置 题解分析
    JS leetcode 杨辉三角Ⅱ 题解分析
    JS leetcode 寻找数组的中心索引 题解分析
    JS leetcode 移除元素 题解分析
    JS leetcode 最大连续1的个数 题解分析
    JS leetcode 两数之和 II
    JS leetcode 反转字符串 题解分析
  • 原文地址:https://www.cnblogs.com/findumars/p/4852515.html
Copyright © 2011-2022 走看看