QT实现任意阶贝塞尔曲线绘制

    bezier曲线在编程中的难点在于求取曲线的系数,如果系数确定了那么就可以用微小的直线段画出曲线。bezier曲线的系数也就是bernstein系数,此系数的性质可以自行百度,我们在这里是利用bernstein系数的递推性质求取:

简单举例

两个点p0,p1 为一阶曲线,系数为 (1-u)p0+u*p1; 将系数存在数组中b[0] =1-u,b[1]=u。

三个点 p0 p1 p2 为二阶曲线,系数(1-u)(1-u)p0+2u(1-u)p1+u*u*p2 可以看出二阶的系数是一届的系数的关系 ((1-u)+u)(b[0]+b[1])。

注意:通过这个公式有没有发现,当u==0的时候这个点就是p0,当u==1的时候这个点就是p2,其他时候点被p1所吸引,也就是p1点的存在会导致(u!=0&&u!=1)的时候生成的点靠近p1

四个点 三阶曲线为:

((1-u)+u)((1-u)+u)(b[0]+b[1])

是不是有种似曾相识的感觉,对了,这就是高中牛顿二项式展开的过程:

二阶贝塞尔曲线实现代码:

QPointF p0(0,0);
QPointF p1(1000,0);
QPointF p2(1000,1000);
QPainterPath path;
path.moveTo(p0);
QPointF pTemp;
for(double t=0; t<1; t+=0.01)  //2次Bezier曲线 
{pTemp  =pow((1-t),2)*p0+2*t*(1-t)*p1+pow(t,2)*p2;path.lineTo(pTemp);
}

没有使用贝塞尔曲线(三个点直接相连)画出来三角形是这样:

使用贝塞尔曲线之后,(1000,0)这个位置的角会圆化:

上图中你会发现曲线不太圆滑,这个你可以调参数precision,主要的问题是它用了贝塞尔曲线之后都不像一个三角形了,我们只想对三角形的角进行圆化。我们可以选择构成三角形角的两边上接近交点位置的两个点,用这个两个点和这两边的交点(三角形的角)生成贝塞尔曲线,效果如下:

我们发现他就是有很多短小的曲线构成的,所以这就是多边形的角圆化的原理。

上面是实现的二阶贝塞尔曲线,但是有时候我们可能会使用其他阶数曲线,所以我们需要改一下代码使得代码更大众化:

/*** @brief createNBezierCurve 生成N阶贝塞尔曲线点* @param src 源贝塞尔控制点,里面有两个点就是一阶,有三个点就是二阶,依次类推* @param dest 目的贝塞尔曲线点* @param precision 生成精度,控制着细小直线的长度,细小直线长度越小模拟出现的圆角越圆滑(此值越小细小直线长度越小)*/
static void createNBezierCurve(const QList<QPointF> &src, QList<QPointF> &dest, qreal precision=0.5)
{if (src.size() <= 0) return;//清空QList<QPointF>().swap(dest);//外侧循环控制(1-u)p0+u*p1中u的值,用来生成多个点for (qreal t = 0; t < 1.0000; t += precision) {int size = src.size();QVector<qreal> coefficient(size, 0);coefficient[0] = 1.000;qreal u1 = 1.0 - t;//里面循环用来生成每一次u改变之后的参数值,参数就是二项展开式,然后把参数和各顶点乘起来就得到贝塞尔曲线的一个顶点for (int j = 1; j <= size - 1; j++) {qreal saved = 0.0;for (int k = 0; k < j; k++){qreal temp = coefficient[k];coefficient[k] = saved + u1 * temp;saved = t * temp;}coefficient[j] = saved;}//最后的贝塞尔顶点QPointF resultPoint;for (int i = 0; i < size; i++) {QPointF point = src.at(i);resultPoint = resultPoint + point * coefficient[i];}dest.append(resultPoint);}
}

然后我来讲讲代码如何实现把三角形的角圆化的:

/*
src就是保存多边形所有顶点的集合,要有序(有序的意思就是按照点的顺序可以形成一个多边形)
dest就是一个空的集合,最后生成的所有点都放在里面,然后按照这些点依次连接最后就是一个角圆化之后的多边形*/
void GeometryViewer::centralHandler(vector<CVector2d>&src, vector<CVector2d>&dest)
{vector<CVector2d>tmp;for (int i = 0; i < src.size(); ++i){   //对于每一个多边形顶点(角),我们需要找到构成这个顶点的两条直线上接近顶点的两个点,用这三个点生成贝塞尔曲线CVector2d pt1 = getLineStart(src[i],src[(src.size() + i - 1) % src.size()]);tmp.push_back(pt1);tmp.push_back(src[i]);CVector2d pt3 = getLineStart(src[i], src[(i + 1) % src.size()]);tmp.push_back(pt3);createNBezierCurve(tmp, dest);tmp.clear();}
}

CVector2d类的功能大致如下:

class CVector2d
{
public:double X,Y;CVector2d(double x,double y):X(x),Y(y){X=x;Y=y;printf("%lf 00**** %lf\n",x,y);}CVector2d operator+(CVector2d y)const{return CVector2d(X+y.X,Y+y.Y);}
};

getLineStart它将返回一个点, 该点是pt1顶点朝着pt2顶点离开m_uiRadius像素。变量fRat保持半径与第i个线段长度之间的比率。还有一项检查可以防止fRat的值超过0.5。如果fRat的值超过0.5, 则两个连续的圆角将重叠, 这将导致较差的视觉效果。

当从点P1到点P2直线行驶并完成距离的30%时, 我们可以使用公式0.7•P1 + 0.3•P2确定位置。通常, 如果我们获得完整距离的一小部分, 并且α= 1表示完整距离, 则当前位置为(1-α)•P1 +α•P2。

这就是GetLineStart方法确定在第(i + 1)方向上距离第i个顶点m_uiRadius像素的点的位置的方式。

 

CVector2d GeometryViewer::getLineStart(CVector2d pt1,CVector2d pt2,double radius=0.0)
{CVector2d pt;double fRat;if(radius==0)fRat = 0.02;else fRat = radius / getDistance(pt1, pt2);if (fRat > 0.5f)fRat = 0.5f;pt.X = (1.0f - fRat)*pt1.X + fRat*pt2.X;pt.Y = (1.0f - fRat)*pt1.Y + fRat*pt2.Y;return pt;
}
//欧几里得距离
double getDistance(CVector2d pt1, CVector2d pt2)
{double fD = (pt1.X - pt2.X)*(pt1.X - pt2.X) +(pt1.Y - pt2.Y) * (pt1.Y - pt2.Y);return sqrt(fD);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/96002.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于JavaWeb和mysql实现校园订餐前后台管理系统(源码+数据库)

一、项目简介 本项目是一套基于JavaWeb和mysql实现网上书城前后端管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

Ubuntu22.04安装Mongodb7.0

Ubuntu安装Mongodb 1.平台支持2.安装MongoDB社区版2.1导入包管理系统使用的公钥2.2为MongoDB创建列表文件2.3重新加载本地包数据库2.4安装MongoDB包1.安装最新版MongoDB2.安装指定版MongoDB 3.运行MongoDB社区版1.目录2.配置文件3.初始化系统4.启动MongoDB5.验证MongoDB是否成功…

无涯教程-Android - Absolute Layout函数

Absolute Layout 可让您指定其子级的确切位置(x/y坐标)&#xff0c;绝对布局的灵活性较差且难以维护。 Absolute Layout - 属性 以下是AbsoluteLayout特有的重要属性- Sr.NoAttribute & 描述1 android:id 这是唯一标识布局的ID。 2 android:layout_x 这指定视图的x坐标…

坦克400 Hi4-T预售价28.5万元起,越野新能源好理解

8月25日&#xff0c;在以“智享蓉城&#xff0c;驭见未来”为主题的成都国际车展上&#xff0c;坦克品牌越野新能源再启新程&#xff0c;首次以全Hi4-T新能源阵容亮相展台&#xff0c;释放坦克品牌加速布局越野新能源的强烈信号。 Hi4-T架构首款落地车型坦克500 Hi4-T上市至今斩…

buildroot修改内核防止清理重新加载办法

当你使用 Buildroot 构建 Linux 内核时&#xff0c;如果对内核文件进行了手动修改&#xff0c;重新执行 Buildroot 的构建过程将会覆盖你所做的修改。这是因为 Buildroot会根据配置重新下载、提取和编译内核。 为了避免在重新构建时覆盖你的修改&#xff0c;可以采取以下两种方…

【Day-27满就是快】代码随想录-二叉树-二叉树的最大深度

给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 ———————————————————————————————————— 1. 递归法 可以使用前序和后序遍历。前序就是…

爬虫逆向实战(二十六)--某某学堂登录

一、数据接口分析 主页地址&#xff1a;某某学堂 1、抓包 通过抓包可以发现数据接口是Account/LoginPost 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现pass是加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无co…

BlockUI专栏目录

文章作者&#xff1a;里海 来源网站&#xff1a;王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C-CSDN博客 简介&#xff1a; BlockUI是一个设计NX对话框的工具&#xff0c;是官方推荐使用的对话框制作方法&#xff0c;能够与NX自身风格相统一&#xff0c;并且在实际…

【算法奥义】最大矩形问题

首先建立一个二维数组&#xff0c;这个二维数组&#xff0c;计算出矩阵的每个元素的左边连续 1 的数量&#xff0c;使用二维数组 left记录&#xff0c;其中left[i][j] 为矩阵第 i 行第 j 列元素的左边连续 1 的数量。 也就是从这个元素开始&#xff0c;从右往左边数有多少个连…

【报错记录】疯狂踩坑之RockyLinux创建Raid1镜像分区,Raid分区在重启后消失了!外加华硕主板使用Raid模式后,硬盘在系统中无法找到问题

前言 为了摆脱对于专业NAS的依赖&#xff0c;我决定专门使用一台Linux服务器安装NAS程序的方式实现NAS功能&#xff0c;这里就需要用到Raid功能&#xff0c;由于目前我只有3块SSD&#xff08;256G500G500G&#xff09;&#xff0c;在ChatGPT的推荐下还是使用一个256G系统盘2块…

ArcGIS Pro实践技术应用、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

12. 自动化项目实战

目录 1. 登录测试 2. 测试首页的帖子列表数不为0 3. 帖子详情页校验 4. 发布帖子 5. 退出登录 自动化项目实施的基本流程如下图所示&#xff1a; 手工测试用例、自动化测试用例。 1. 登录测试 校验登录后主页显示的用户名称和登录时输入的用户名是否相等。 public class…