绘制过多点的曲线意义重大。但通过试验,QT的PainterPath不是很如意。当多段曲线围成一个区域时,PainterPath内并不包含该区域的所有面积,只包含曲线和其弦构成的面积。

为了解决这一问题,采用如下方法:

1. 生成自己的bezier曲线点集

2. 将多个bezier曲线头尾相联,形成整个polygon的点集

3. 将这个polygon放入一个PainterPath,然后绘制;

4. 这个PainterPath返回留待下次使用。

下面是代码:

1. 头文件graphic.h

#ifndef GRAPHIC_H
#define GRAPHIC_H
#include <QPainter>
#include <QPoint>
#include <QColor>
#include <QVector>
//step是步长,即t每次的递增量,traceSet返回本曲线的所有生成点
void getBezier3(const QPointF& startPos, const QPointF& controlPos1, const QPointF& controlPos2, const QPointF& endPos,
                const double step, QVector<QPointF>& traceSet);
//画一个多边形的外接曲线,points是多边形的顶点集合,方向是CCW或CW;k_c是连接处的尖锐度,越大越光滑,通常选0.6;path是返回量,它是一个包含了外接曲线所有点的多边形区域,可用于
//测试一个点是否在这个区域内,或两个区域是否相交等,还可以完成path的绘制。
void drawEncloseCurve(QPainter& painter, const QVector<QPoint>& points, float k_c, const QColor& color, int strokWidth, QPainterPath& path);
#endif // GRAPHIC_H

2. 绘制实现graphic.cpp

#include "graphic.h"
//求两点距离
double distance(const QPoint& p1, const QPoint& p2)
{
        return sqrt(((p1.x() - p2.x()) * (p1.x() - p2.x()) + (p1.y() - p2.y()) * (p1.y() - p2.y())));
}
//根据比例调整点的位置
QPoint ratioPointConvert(const QPoint& p1, const QPoint& p2, const double ratio)
{
        QPoint p;
        p.setX((int) (ratio * (p1.x() - p2.x()) + p2.x()));
        p.setY((int) (ratio * (p1.y() - p2.y()) + p2.y()));
        return p;
}

void getBezier3(const QPointF& startPos, const QPointF& controlPos1, const QPointF& controlPos2, const QPointF& endPos,
                const double step, QVector<QPointF>& traceSet)
{
    double t = 0.0;
    double x_t, y_t;
    while (t <= 1.0) {
        x_t = startPos.x() * (1 - t) * (1 - t) * (1 - t) + 3 * controlPos1.x() * t * (1 - t) * (1 - t) + 3 * controlPos2.x() * t * t * (1 - t) + endPos.x() * t * t * t;
        y_t = startPos.y() * (1 - t) * (1 - t) * (1 - t) + 3 * controlPos1.y() * t * (1 - t) * (1 - t) + 3 * controlPos2.y() * t * t * (1 - t) + endPos.y() * t * t * t;
        traceSet.push_back(QPointF(x_t, y_t));
        t += step;
    }
}

void drawEncloseCurve(QPainter& painter, const QVector<QPoint>& points, float k_c, const QColor& color, int strokWidth, QPainterPath& path)
{
    //path.clear();
    int size = points.size();
    QVector<QPoint> midpoints;
    // 计算中点
    for(int i = 0; i < size; i++)
    {
        int j = (i + 1) % size;
        midpoints.push_back(QPoint((points[i].x() + points[j].x()) / 2, (points[i].y() + points[j].y()) / 2));
    }
    // 计算比例点
    QVector<QPoint> ratioPoints;
    for(int i = 0; i < size; i++)
    {
        int j = (i + 1) % size;
        int m = (i + 2) % size;
        double l1 = distance(points[i], points[j]);
        double l2 = distance(points[j], points[m]);
        double ratio = l1 / (l1 + l2);
        ratioPoints.push_back(ratioPointConvert(midpoints[i], midpoints[j], ratio));
    }

    // 移动线段,计算控制点
    QVector<QPoint> controlPoints;
    for (int i = 0; i < size; i++)
    {
        QPoint ratioPoint = ratioPoints[i];
        QPoint verPoint = points[(i + 1) % size];
        int dx = ratioPoint.x() - verPoint.x();
        int dy = ratioPoint.y() - verPoint.y();
        QPoint controlPoint1 = QPoint(midpoints[i].x() - dx, midpoints[i].y() - dy);
        QPoint controlPoint2 = QPoint(midpoints[(i + 1) % size].x() - dx, midpoints[(i + 1) % size].y() - dy);
        controlPoints.push_back(ratioPointConvert(controlPoint1, verPoint, k_c));
        controlPoints.push_back(ratioPointConvert(controlPoint2, verPoint, k_c));
    }

    // 用三阶贝塞尔曲线连接顶点
    //QPainterPath path;
    QVector<QPointF> polypoints;
    QPen pen;
    pen.setColor(color);
    pen.setWidth(strokWidth);
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    painter.setPen(pen);

    for (int i = 0; i < size; i++)
    {
        QPoint startPoint = points[i];
        QPoint endPoint = points[(i + 1) % size];
        QPoint controlPoint1 = controlPoints[(i * 2 + controlPoints.size() - 1) % controlPoints.size()];
        QPoint controlPoint2 = controlPoints[(i * 2) % controlPoints.size()];
//        path.moveTo(startPoint.x(), startPoint.y());
//        path.cubicTo(controlPoint1.x(), controlPoint1.y(), controlPoint2.x(), controlPoint2.y(), endPoint.x(), endPoint.y());
        QVector<QPointF> bezier;
        getBezier3(startPoint, controlPoint1, controlPoint2, endPoint, 0.01, bezier);
        bezier.removeLast();
        polypoints += bezier;
    }
    QPolygonF poly(polypoints);
    path.addPolygon(poly);
    painter.drawPath(path);
}

3. 测试(mainwindow.cpp)

测试时发现了问题:当Qdebug()<< 输入中文时,调试直接跳走,无法继续步进,但如果改成英文,则没有问题。

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制封闭的外接曲线
    painter.setRenderHint(QPainter::Antialiasing, true);
    QVector<QPoint> points;
    points.push_back(QPoint(200, 200));
    points.push_back(QPoint(400, 100));
    points.push_back(QPoint(600, 200));
    points.push_back(QPoint(400, 400));
    drawEncloseCurve(painter, points, 0.6, QColor(Qt::black), 2, npath);

    painter.save();
    painter.translate(400, 0);
    painter.fillPath(npath, QBrush(Qt::green));
    painter.restore();
} 测试鼠标是否在这个区域中
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        QPointF p = event->pos();
        if(npath.contains(p))
            qDebug() << ("you hit it path");
        //通过试验,有时用npath测试无效,但poly总是有效
        QPolygonF poly = npath.toFillPolygon();

        if(poly.containsPoint(p, Qt::WindingFill))
        {
            qDebug() << ("you hit it poly");
            startDrawing = false;
        }
        else
        {
            startDrawing = true;
            startPos = event->pos();
        }
    }
}
4.结果:
通过四个点的封闭曲线,并通过了鼠标选取测试

QT下过多点的曲线绘制的更多相关文章

  1. CustomPlot 在Qt下 鼠标点击曲线 显示当前坐标

    此次记录主要是为了下次使用时能回忆起来才做得笔记,若有需改进的地方,请不吝珠玉. widget.cpp #include "widget.h" #include "ui_ ...

  2. 【转】基于Qt, TUIO和TSLIB的嵌入式Linux下的多点触摸设计

    这个教程描述了在嵌入式linux下使用Qt如何设置一个支持多点触摸和单点触摸的输入系统.这里假定你已经有了对应的驱动程序,驱动可以从触摸屏的厂商那里获得或者使用一个linux 内核源码中已经存在的驱动 ...

  3. [转帖]MATLAB曲线绘制及颜色类型

    信号源产生的方法 来源:http://www.2cto.com/kf/201401/270494.html  matlab的checkerboard说明,GOOD! 来源:http://www.chi ...

  4. paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制

    源地址:http://blog.csdn.net/attilax/article/details/12343625 paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制 效果图: ...

  5. iOS 使用贝塞尔曲线绘制路径

    使用贝塞尔曲线绘制路径 大多数时候,我们在开发中使用的控件的边框是矩形,或者做一点圆角,是使得矩形的角看起来更加的圆滑. 但是如果我们想要一个不规则的图形怎么办?有人说,叫UI妹子做,不仅省事,还可以 ...

  6. OpenGL 实践之贝塞尔曲线绘制

    说到贝塞尔曲线,大家肯定都不陌生,网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图. 以下两个是比较经典的动图了. 二阶贝塞尔曲线: 三阶贝塞尔曲线: 由于在工作中经常要和贝塞尔曲线打交道,所以简 ...

  7. [机器学习]-分类问题常用评价指标、混淆矩阵及ROC曲线绘制方法

    分类问题 分类问题是人工智能领域中最常见的一类问题之一,掌握合适的评价指标,对模型进行恰当的评价,是至关重要的. 同样地,分割问题是像素级别的分类,除了mAcc.mIoU之外,也可以采用分类问题的一些 ...

  8. 【转】Qt下使用glut库

    ps:这个说的很明白,尤其是win10环境下用mingw环境时编程时碰到的问题, 1.加 windows.h 2.在.pro 添加libs     博文地址:Qt下使用glut库   本人使用的环境 ...

  9. ROC曲线绘制

    ROC 曲线绘制 个人的浅显理解:1.ROC曲线必须是针对连续值输入的,通过选定不同的阈值而得到光滑而且连续的ROC曲线,故通常应用于Saliency算法评价中,因为可以选定0~255中任意的值进行阈 ...

随机推荐

  1. Cloneable注解使用

    使用 clone()方法的类必须 implement Cloneable 如果没有继承,clone()方法会报错 java.lang.CloneNotSupportedException异常

  2. luogu3651 展翅翱翔之时 (はばたきのとき)[基环树+贪心]

    考前随便做点水题愉♂悦身心 有助于退役 这题意思其实就是说要把外向基环树森林改成一个环的最小代价. 依照套路,先对每棵基环树的树做dp,这里因为要是环,要把所有的树都拆成链,然后连接.所以考虑以最小代 ...

  3. USC-- compute shader ps vs

    http://cdn.imgtec.com/sdk-documentation/PowerVR%20Compute%20Development%20Recommendations.pdf powerV ...

  4. 对List<Map>里的map的某个属性重复的值进行处理的方法

    package test; import java.util.*;import java.util.stream.Collectors; public class Test5 { public sta ...

  5. 03 JavaScript的使用

    01 注册界面的校验 <!-- 作者:offline 时间:2018-09-05 描述:通常在CSS中使用类选择器,在JS中使用id选择器,两者区分开. 在页面跳转时要先把要跳转的页面用浏览器打 ...

  6. 如何打开Mac OSX 终端的颜色

    如何打开Mac OSX 终端的颜色 听语音 | 浏览:8453 | 更新:2015-12-15 16:48 1 2 3 4 5 6 7 分步阅读 Mac 终端默认颜色很单一,文件夹和文件无法区分,可以 ...

  7. vim 自动代码格式调整

    1,gg 跳转到第一行 2,shift+v 转到可视模式 3,shift+g 全选 4,按下神奇的 =

  8. 第四届西安邮电大学acm-icpc校赛 猜球球

    题目描述 六一到了,为了庆祝这个节日,好多商家都推出了很多好玩的小游戏.Tongtong看到了一个猜球球的游戏,有n种除了颜色之外完全相同的球,商家从中拿出来一个球球放到了箱子里,已知第i种颜色的球出 ...

  9. HGOI20191114 CSP模拟赛 反思

    Problem A 宇宙魔方 有一个$N \times N \times N$的魔方,每一次操作可以整体转动该魔方,也可以对于一层整体+X. 给出最后魔方的最终状态,其中有一个位置为-1.利用其它位置 ...

  10. jQuery系列(十四):jQuery中的ajax

    1.什么是ajax AJAX = 异步的javascript和XML(Asynchronous Javascript and XML) 简言之,在不重载整个网页的情况下,AJAX通过后台加载数据,并在 ...