绘制过多点的曲线意义重大。但通过试验,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. Solving Docker permission denied while trying to connect to the Docker daemon socket

    The error message tells you that your current user can’t access the docker engine, because you’re la ...

  2. python一些方便excel行操作的函数(一)

    import collections class headhandler(): def __init__(self,mylist): self.mystorage={} self.mylist = m ...

  3. BZOJ 2124 (线段树 + hash)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2124 题意:给一个1到N的排列{Ai},询问是否存在1<=p1<p2< ...

  4. 洛谷P1026 统计单词个数【区间dp】

    题目:https://www.luogu.org/problemnew/show/P1026 题意: 给定一个字符串,要求把他分成k段.给定s个单词,问划分成k段之后每段中包含的单词和最大是多少. 一 ...

  5. 初步学习HashTable(哈希表或者散列链表)

    初次接触哈希表,我谈谈自己对哈希表的一些理解,可能有误,还望指正. 对于哈希表,存放的数据是键值对<key,value>.是按照键值来索引的,键key可以是字符串.单个字符.整形数等,值v ...

  6. deepin Linux 安装+工作学习配置

    一 安装 在官网下载 U盘安装,神舟优雅x4开机按F7,选择U盘启动. U盘安装器在下载的镜像文件中. 二 配置 升级最新系统 设置root用户密码: dawn@dawn-PC:~$ sudo pas ...

  7. shell 读取文本并访问mysql/redis

    #!/bin/bash File="redeemcode.csv" #File=$ database="d_redeem_info" echo "ch ...

  8. vue 循环Redio

    标准用法,做个笔记.(图示是elementUI,其他框架大同小异) <el-radio-group @change="changePayHandler" v-model=&q ...

  9. python struct的使用例子

    import struct i = 1024 # s0为一个字符串,长度为4,即占四个字节,这样方便传输与保存二进制数据. s0 = struct.pack(">I", i) ...

  10. Netfilter 之 iptable_filter

    概述 本文主要对filter表的初始化流程,以及钩子函数的规则match流程的源码进行分析: 源码分析 所在钩子点: /* 在LOCAL_IN,FORWARD, LOCAL_OUT钩子点工作 */ # ...