zoukankan      html  css  js  c++  java
  • QT绘制B样条曲线

    ²  贝塞尔曲线

    贝塞尔曲线是通过一组多边折线的各顶点来定义。在各顶点中,曲线经过第一点和最后一点,其余各点则定义曲线的导数、阶次和形状。第一条和最后一条则表示曲线起点和终点的切线方向。

    ²  B样条曲线

    针对贝塞尔曲线存在的一些缺点,数学家们提出了B样条方法,在保留贝塞尔全部优点的同时,克服可贝塞尔方法的弱点。

    1)      二次B样条曲线

    2)      三次B样条曲线

     QT中的QPainter提供了绘制贝塞尔曲线的相关API:

    void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint)

    void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)

    void QPainter::drawPath(const QPainterPath &path)

    Widget.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
     
    /////////////////////////////////////////////////////////////
    /// @file   Widget.h
    /// @brief  绘制B样条曲线Widget类
    ///
    /// 通过鼠标点击来绘制B样条曲线
    /// @author Michael Joessy
    /// @date   2019-07-02
    /////////////////////////////////////////////////////////////
    #ifndef WIDGET_H
    #define WIDGET_H

    #include <QWidget>
    #include <QVector>

    class Widget : public QWidget
    {
        Q_OBJECT

    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();

    protected:
        
    virtual void mousePressEvent(QMouseEvent *event);
        
    virtual void mouseReleaseEvent(QMouseEvent *event);
        
    virtual void mouseDoubleClickEvent(QMouseEvent *event);
        
    virtual void mouseMoveEvent(QMouseEvent *event);
        
    virtual void paintEvent(QPaintEvent *event);

    private:
        
    void drawSpline();
        qreal N(
    int k, int i, qreal u);
        qreal N1(
    int i, qreal u);
        qreal N2(
    int i, qreal u);
        qreal N3(
    int i, qreal u);

    private:
        QVector<QPointF>    m_ctrlPoints;       
    // 控制点
        QVector<QPointF>    m_curvePoints;      // 曲线上的点
    };

    #endif // WIDGET_H
    Widget.pp 
    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
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
     
    #include "Widget.h"
    #include <QMouseEvent>
    #include <QPainter>
    #include <cmath>

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
    }

    Widget::~Widget()
    {

    }

    void Widget::mousePressEvent(QMouseEvent *event)
    {
        
    // 单击鼠标左键获取控制点
        if (event->buttons() == Qt::LeftButton){
            m_ctrlPoints.push_back(event->pos());
        }
        
    // 单击鼠标右键清空控制点
        else if (event->buttons() == Qt::RightButton) {
            m_ctrlPoints.clear();
        }
        update();
    }

    void Widget::mouseReleaseEvent(QMouseEvent *event)
    {

    }

    void Widget::mouseDoubleClickEvent(QMouseEvent *event)
    {

    }

    void Widget::mouseMoveEvent(QMouseEvent *event)
    {

    }

    void Widget::paintEvent(QPaintEvent *event)
    {
        drawSpline();
    }

    void Widget::drawSpline()
    {
        QPainter painter(
    this);
        
    int currentK = 3;       // 阶数
        m_curvePoints.clear();
        
    for (qreal u = currentK; u < m_ctrlPoints.size(); u += 0.01){
            QPointF pt(
    0.00.0);
            
    for (int i = 0; i < m_ctrlPoints.size(); ++i){
                QPointF pts = m_ctrlPoints[i];
                pts *= N(currentK, i, u);
                pt += pts;
            }
            m_curvePoints.push_back(pt);
        }

        
    // draw control points
        QPen ctrlPen1(QColor(00255));
        ctrlPen1.setWidth(
    5);
        painter.setPen(ctrlPen1);
        
    for (int i = 0; i < m_ctrlPoints.size(); ++i){
            painter.drawPoint(m_ctrlPoints[i]);
        }
        
    // draw control lines
        QPen ctrlPen2(QColor(25500));
        ctrlPen2.setWidth(
    1);
        ctrlPen2.setStyle(Qt::DashDotDotLine);
        painter.setPen(ctrlPen2);
        
    for (int i = 0; i < m_ctrlPoints.size() - 1; ++i){
            painter.drawLine(m_ctrlPoints[i], m_ctrlPoints[i + 
    1]);
        }
        
    // draw spline curve
        QPen curvePen(QColor(000));
        curvePen.setWidth(
    2);
        painter.setPen(curvePen);
        
    for (int i = 0; i < m_curvePoints.size() - 1; ++i){
            painter.drawLine(m_curvePoints[i], m_curvePoints[i + 
    1]);
        }
    }

    qreal Widget::N(
    int k, int i, qreal u)
    {
        
    switch (k) {
        
    case 1:
            
    return N1(i, u);
        
    case 2:
            
    return  N2(i, u);
        
    case 3:
            
    return  N3(i, u);
        
    default:
            
    break;
        }
    }

    qreal Widget::N1(
    int i, qreal u)
    {
        qreal t = u - i;
        
    if (0 <= t && t < 1){
            
    return t;
        }
        
    if (1 <= t && t < 2){
            
    return 2 - t;
        }
        
    return 0;
    }

    qreal Widget::N2(
    int i, qreal u)
    {
        qreal t = u - i;
        
    if (0 <= t && t < 1){
            
    return 0.5 * t * t;
        }
        
    if (1 <= t && t < 2){
            
    return 3 * t - t * t -1.5;
        }
        
    if (2 <= t && t < 3){
            
    return 0.5 * pow(3 - t, 2);
        }
        
    return 0;
    }

    qreal Widget::N3(
    int i, qreal u)
    {
        qreal t = u - i;
        qreal a = 
    1.0 / 6.0;
        
    if (0 <= t && t < 1){
            
    return a * t * t * t;
        }
        
    if (1 <= t && t < 2){
            
    return a * (-3 * pow(t - 13) + 3 * pow(t - 12) + 3 * (t - 1) + 1);
        }
        
    if (2 <= t && t < 3){
            
    return a * (3 * pow(t - 23) - 6 * pow(t - 22) + 4);
        }
        
    if (3 <= t && t < 4){
            
    return a * pow(4 - t, 3);
        }
        
    return 0;
    }

    上述算法有点简单,https://github.com/vkorchagin/animated-b-spline提供了比较好的算法例子,值得参考。

    给定B样条曲线的控制点。 这些点在屏幕上移动,从而使样条动画。
    B样条通过de Boor算法转换为合成Bezier曲线。 贝塞尔曲线用de Casteljau算法进行插值。
    特征:
    添加和删除控制点。
    通过de Casteljau算法更改插值质量。
    切换抗锯齿。
    不断变化的动画速度。
    切换可见点和线。

  • 相关阅读:
    第十四周学习进度总结
    第十三周学习进度总结
    第十二周学习进度总结
    第十一周学习进度总结
    第十周学习进度总结
    第九周学习进度总结
    第八周学习进度总结
    day16-机器学习十讲第一讲
    day15-验证码识别
    day14-卷积网络识别手写数字
  • 原文地址:https://www.cnblogs.com/MakeView660/p/11122320.html
Copyright © 2011-2022 走看看