B样条数据点反求控制点绘制曲线(源码)
一、软件功能需求
1)所设计的软件应具有图形化用户界面(GUI);
2)用户在软件界面上可用随机数方式或手工方式输入若干曲线或曲面的数据点,例如起点、终点、列表型值点等,对于曲线,还可设置步长参数;对于曲面,还可设置步长与行距参数;曲线或曲面的类型不限。
3)具有“绘制理想图形”按钮,用户完成数据点与参数输入后,点击该按钮,软件可绘制出理想的曲线或曲面,若该曲线或曲面有特征多边形,则还能通过型值点反算出控制顶点,并绘制出对应的特征多边形;
4)具有“生成加工路径”按钮,点击该按钮,可根据设定的步长与行距参数自动生成加工该曲线或曲面的刀具路径;
5)具有“生成加工程序”按钮,点击该按钮,可自动生成一个实现该曲线或曲面加工的G代码数控加工程序文件。
二、B样条反求控制点原理
参考书籍:计算机辅助几何设计与非均匀有理B样条(一般学校的电子图书馆可以在线阅读)
P212页:B样条递推定义
P237页:齐次B样条曲线的节点矢量
P257页:反算三次B样条插值曲线的控制顶点
P259页:边界条件:抛物线条件
二、软件实现
软件平台:Windows7 Qt5.9.3 语言:C++
由于默认界面框的原点在左上角,并且向下为Y+,向右为x+,所以在画图或者鼠标捕捉时,要将其转化到正常的直角坐标系,并绘制坐标系。
QPainter* painter = new QPainter(this);
painter->setWindow(-width()/2,height()/2,width(),-height()); //设置坐标原点在屏幕的正中心
// painter->drawLine(-296,0,296,0);
// painter->drawLine(0,296,0,-296);
//绘制x轴
QPointF startPoint(-295,0);
QPointF endPoint(295,0);
double x1, y1, x2, y2; //箭头的两点坐标
//求得箭头两点坐标
calcVertexes(startPoint.x(), startPoint.y(), endPoint.x(), endPoint.y(), x1, y1, x2, y2);
painter->drawLine(startPoint, endPoint);//绘制线段
painter->drawLine(endPoint.x(), endPoint.y(), x1, y1);//绘制箭头一半
painter->drawLine(endPoint.x(), endPoint.y(), x2, y2);//绘制箭头另一半
//绘制Y轴
startPoint.setX(0);
startPoint.setY(-295);
endPoint.setX(0);
endPoint.setY(295);
//求得箭头两点坐标
calcVertexes(startPoint.x(), startPoint.y(), endPoint.x(), endPoint.y(), x1, y1, x2, y2);
painter->drawLine(startPoint, endPoint);//绘制线段
painter->drawLine(endPoint.x(), endPoint.y(), x1, y1);//绘制箭头一半
painter->drawLine(endPoint.x(), endPoint.y(), x2, y2);//绘制箭头另一半
void Palette::calcVertexes(double start_x, double start_y, double end_x, double end_y, double& x1, double& y1, double& x2, double& y2)
{
double arrow_lenght_ = 10;//箭头长度,一般固定
double arrow_degrees_ = 0.5;//箭头角度,一般固定
double angle = atan2(end_y - start_y, end_x - start_x) + 3.1415926;//
x1 = end_x + arrow_lenght_ * cos(angle - arrow_degrees_);//求得箭头点1坐标
y1 = end_y + arrow_lenght_ * sin(angle - arrow_degrees_);
x2 = end_x + arrow_lenght_ * cos(angle + arrow_degrees_);//求得箭头点2坐标
y2 = end_y + arrow_lenght_ * sin(angle + arrow_degrees_);
}
采用随机数生成8个随机点,对第一组随机数进行排序,第二组随机数不排序,填充到QTableWidget。
void MainWindow::on_general_random_clicked()
{
QVector <int> a; //定义一个vector容器
srand((int)time(NULL)); //时间作为种子
for (int i=0;i<8;i++)
{
a.append(rand()%296-148); //向vector后面追加元素,区间根据显示区域设定
}
qSort(a.begin(),a.end()); //对vector从小到大排序
for(int i=0;i<8;i++) //向X坐标写入数据
{
ui->model->setItem(i,0,new QTableWidgetItem(QString("%1").arg(a[i])));
}
a.remove(0,8); //删去第一列
srand((int)time(NULL));
for (int i=0;i<8;i++)
{
a.append(rand()%296-148);
}
for(int i=0;i<8;i++)
{
ui->model->setItem(i,1,new QTableWidgetItem(QString("%1").arg(a[i])));
ui->model->setItem(i,2,new QTableWidgetItem(QString("%1").arg(0)));
}
}
获取数据点,根据数据点反求控制点。
void MainWindow::on_general_curve_clicked()
{
QPoint point;
QPoint start,end;
QString QTW_data1,QTW_data2;
double a1,a2;
//提取数据表中数据
for(int i=0; i<8; i++)
{
QTW_data1=ui->model->item(i, 0)->text();
QTW_data2=ui->model->item(i, 1)->text();
a1=QTW_data1.toDouble();
a2=QTW_data2.toDouble();
point.setX(a1);
point.setY(a2);
if(i==0)
{
start.setX(a1);
start.setY(a2);
}
if(i==7)
{
end.setX(a1);
end.setY(a2);
}
palette->typepoints.append(point);
}
/*
此段程序将数据点反求出控制点,八个数据点
*/
double l[7]; //存放数据点之间的距离,l[0]代表第一个数据点到第二个数据点之间的距离
double L=0;//存放总长度
for(int i=0;i<7;i++)
{
l[i]=sqrt(pow((palette->typepoints[i+1].x()-palette->typepoints[i].x()),2)+
pow((palette->typepoints[i+1].y()-palette->typepoints[i].y()),2)); //两点之间距离公式
L=L+l[i];
}
//节点矢量计算
for(int i=0;i<4;i++)
{
palette->u[i]=0;
}
for(int i=0;i<4;i++)
{
palette->u[i+10]=1;
}
palette->u[4]=l[0]/L;
palette->u[5]=(l[0]+l[1])/L;
palette->u[6]=(l[0]+l[1]+l[2])/L;
palette->u[7]=(l[0]+l[1]+l[2]+l[3])/L;
palette->u[8]=(l[0]+l[1]+l[2]+l[3]+l[4])/L;
palette->u[9]=(l[0]+l[1]+l[2]+l[3]+l[4]+l[5])/L;
for(int i=0;i<14;i++)
{
qDebug()<<"u[i]="<<palette->u[i]<<endl;
}
double detal[13];
for(int i=0;i<13;i++)
{
detal[i]=palette->u[i+1]-palette->u[i];
qDebug()<<"detal[i]="<<detal[i]<<endl;
}
//根据节点矢量,反算控制点,其中需要使用矩阵求逆
double a[8],b[8],c[8],e[8],f[8]; //角标0-8
a[0]=1-(detal[3]*detal[4])/pow((detal[3]+detal[4]),2);
b[0]=(detal[3]/(detal[3]+detal[4]))*(detal[4]/(detal[3]+detal[4])-detal[3]/(detal[3]+detal[4]+detal[5]));
c[0]=(pow((detal[3]),2))/((detal[3]+detal[4])*(detal[3]+detal[4]+detal[5]));
e[0]=(1.0/3)*(palette->typepoints[0].x()+2*palette->typepoints[1].x()); //X轴坐标
f[0]=(1.0/3)*(palette->typepoints[0].y()+2*palette->typepoints[1].y()); //Y轴坐标
a[7]=-(pow((detal[9]),2))/((detal[8]+detal[9])*(detal[8]+detal[8]+detal[9]));
b[7]=(detal[9]/(detal[8]+detal[9]))*(detal[9]/(detal[8]+detal[8]+detal[9])-detal[8]/(detal[8]+detal[9]));
c[7]=detal[8]*detal[9]/pow((detal[8]+detal[9]),2)-1;
e[7]=-(1.0/3)*(palette->typepoints[7].x()+2*palette->typepoints[6].x());
f[7]=-(1.0/3)*(palette->typepoints[7].y()+2*palette->typepoints[6].y());
for(int i=1;i<7;i++)
{
// e[8]=palette->typepoints[i].x();
a[i]=(pow((detal[i+3]),2))/(detal[i+1]+detal[i+2]+detal[i+3]);
b[i]=detal[i+3]*(detal[i+1]+detal[i+2])/(detal[i+1]+detal[i+2]+detal[i+3])+detal[i+2]*(detal[i+3]+detal[i+4])/(detal[i+2]+detal[i+3]+detal[i+4]);
c[i]=pow((detal[i+2]),2)/(detal[i+2]+detal[i+3]+detal[i+4]);
e[i]=(detal[i+2]+detal[i+3])*palette->typepoints[i].x();
f[i]=(detal[i+2]+detal[i+3])*palette->typepoints[i].y();
}
for(int i=0;i<8;i++)
{
qDebug()<<"a[i]="<<a[i]<<"b[i]="<<b[i]<<"c[i]="<<c[i]<<"e[i]"<<e[i]<<endl;
}
double matrix[8][8];
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
matrix[i][j]=0;
qDebug()<<matrix[i][j]<<" ";
}
qDebug()<<endl;
}
qDebug()<<"_________________________"<<endl;
/*对系数矩阵赋值*/
matrix[0][0]=a[0];
matrix[0][1]=b[0];
matrix[0][2]=c[0];
for(int i=1;i<7;i++)
{
matrix[i][i-1]=a[i];
matrix[i][i]=b[i];
matrix[i][i+1]=c[i];
}
matrix[7][5]=a[7];
matrix[7][6]=b[7];
matrix[7][7]=c[7];
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
qDebug()<<matrix[i][j]<<" ";
}
}
qDebug()<<endl;
double matrix_after[N][N];
qDebug()<<"_________________"<<endl;
matrix_inv(matrix,matrix_after); //求逆矩阵
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
qDebug()<<matrix_after[i][j]<<" ";
}
}
qDebug()<<endl;
double sum_x[8]={},sum_y[8]={};
palette->ctrlPoints.append(start);
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
sum_x[i]=sum_x[i]+matrix_after[i][j]*e[j];
sum_y[i]=sum_y[i]+matrix_after[i][j]*f[j];
}
palette->ctrlPoints.append(QPoint(sum_x[i],sum_y[i]) );
qDebug()<<"sum_x[i]="<<sum_x[i]<<"sum_y[i]="<<sum_y[i]<<endl;
}
palette->ctrlPoints.append(end);
qDebug()<<palette->ctrlPoints<<endl;
palette->generateCurve(); //根据控制点绘制型值点
palette->update(); //更新画板
}
矩阵求逆C++实现:
void MainWindow::matrix_inv(double matrix_before[N][N],double matrix_after[N][N])
{
bool flag;//标志位,如果行列式为0,则结束程序
flag=GetMatrixInverse(matrix_before,N,matrix_after);
}
//按第一行展开计算|A|
double MainWindow::getA(double arcs[N][N],int n)
{
if(n==1)
{
return arcs[0][0];
}
double ans = 0;
double temp[N][N]={0.0};
int i,j,k;
for(i=0;i<n;i++)
{
for(j=0;j<n-1;j++)
{
for(k=0;k<n-1;k++)
{
temp[j][k] = arcs[j+1][(k>=i)?k+1:k];
}
}
double t = getA(temp,n-1);
if(i%2==0)
{
ans += arcs[0][i]*t;
}
else
{
ans -= arcs[0][i]*t;
}
}
return ans;
}
//计算每一行每一列的每个元素所对应的余子式,组成A*
void MainWindow::getAStart(double arcs[N][N],int n,double ans[N][N])
{
if(n==1)
{
ans[0][0] = 1;
return;
}
int i,j,k,t;
double temp[N][N];
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
for(k=0;k<n-1;k++)
{
for(t=0;t<n-1;t++)
{
temp[k][t] = arcs[k>=i?k+1:k][t>=j?t+1:t];
}
}
ans[j][i] = getA(temp,n-1); //此处顺便进行了转置
if((i+j)%2 == 1)
{
ans[j][i] = - ans[j][i];
}
}
}
}
//得到给定矩阵src的逆矩阵保存到des中。
bool MainWindow::GetMatrixInverse(double src[N][N],int n,double des[N][N])
{
double flag=getA(src,n);
double t[N][N];
if(0==flag)
{
// cout<< "原矩阵行列式为0,无法求逆。请重新运行" <<endl;
return false;//如果算出矩阵的行列式为0,则不往下进行
}
else
{
getAStart(src,n,t);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
des[i][j]=t[i][j]/flag;
}
}
}
return true;
}
根据控制点绘制B样条曲线:
void Palette::generateCurve()
{
curvePoints.clear();
int i=3;
for(double u1=0; u1<1; u1+=0.001)
{
QPointF tmp(0,0);
if(u1>u[i+1])
{i=i+1;}
for(int k=0; k<4;k++)
{
QPointF t = ctrlPoints[i-k];
t*=Nu(i-k,u1,3);
tmp+=t;
}
//qDebug()<<"tmp="<<tmp;
curvePoints.push_back(tmp);
}
}
double Palette::Nu(int i,double u2,int k)
{
if(k==0)
{
if((u2>=u[i])&&(u2<u[i+1]))
return 1;
else
return 0;
}
else{
double a1=(u2-u[i]),c1=(u[i+k]-u[i]);
double b1=(u[i+k+1]-u2),d1=(u[i+k+1]-u[i+1]);
if(d1<0.001)
{
b1=0;
d1=1;
}
if(c1<0.001)
{
a1=0;
c1=1;
}
double nu=(a1/c1)*Nu(i,u2,k-1)+(b1/d1)*Nu(i+1,u2,k-1);
return nu;
}
}
运行结果
致谢:强强(B样条原理)、瑶哥(矩阵求逆,递推公式绘图)
源码:https://download.csdn.net/download/qq_32059343/10927125
————————————————
版权声明:本文为CSDN博主「东辰叶落」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32059343/article/details/86408359
B样条数据点反求控制点绘制曲线(源码)相关推荐
- Framework学习之路(一)—— UI绘制深入源码分析
Framework学习之路(一)-- UI绘制深入源码分析 本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用.由于笔者经验尚浅,水平也有限,所以会存在很多不足的 ...
- C++floyd warshall算法求最短路径(附完整源码)
C++floyd warshall算法求最短路径 floyd warshall算法求最短路径的完整源码(定义,实现,main函数测试) floyd warshall算法求最短路径的完整源码(定义,实现 ...
- C++ Opengl 绘制字体源码
C++ Opengl 绘制字体源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方 ...
- C++ Opengl绘制3D源码
C++ Opengl绘制3D源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库 ...
- C++Opengl绘制三角形源码
C++Opengl绘制三角形源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库 ...
- 使用U盘传数据时操作系统做了什么(源码分析)
一.背景 学习linux文件系统时考虑一个具体的问题:我们经常会用U盘传输东西到计算机中.当我们把U盘插入到一台计算机上,并且从U盘中复制文件到计算机里,然后卸载U盘,将U盘拔出.操作系统在这一连串过 ...
- 微信反编译(二)源码还原
概览 我们知道,前端 Web 网页编程采用的是 HTML + CSS + JS 这样的组合,其中 HTML 是用来描页面的结构,CSS 用来描述页面的样子,JS 通常用来处理页面逻辑和用户的交互.类似 ...
- Python反反爬之JS混淆---源码乱码(详细教程)
写在前面 很早之前在吾爱破解论坛上看见了[猿人学]Web端爬虫攻防大赛,当时进入他们官网的时候,比赛已经结束了.看着那些题目还挺有意思的,但由于各种原因一直没有机会去做那些题目.最近比较闲,就去把猿人 ...
- (上)Vue+Echarts构建可视化大数据平台实战项目分享(附源码)
前言 分享之前我们先来普及一下什么是数据可视化?数据可视化可以把数据从冰冷的数字转换成图形,揭示蕴含在数据中的规律和道理.数据可视化通俗来说就是:数据的展示.处理和分析.目的是借助于图形化手段,清晰有 ...
最新文章
- linux upx 报错 NotCompressibleException
- windows系统tomcat日志输出至catalina.out配置说明
- linux下ARP防御
- java ee jsp_EE JSP:Servlet的反向外套
- Python 08 面向对象
- ueditor的配置和使用
- SQL Server商业智能–在已建立的数据仓库中扩展事实表
- xe android 联系人,Delphi XE 开发android后台服务例程
- 计算机网络管理员初级是几级,计算机网络管理员初级操作技能考核试题
- HTML5期末大作业:动漫A网站设计——动画漫展学习资料电影模板(6页) 网页设计作业 _ 动漫网页设计作业,网页设计作业 _ 动漫网页设计成品,网页设计作业 _ 动漫网页设计成品模板下载
- ps有一款比较好用的插件也就是调色插件用过吗
- 不同手机型号图文预览_各种手机型号的查询方法(国外英文资料).doc
- 事后诸葛亮(葫芦娃队)
- 适合有编程基础的人看的《韩顺平零基础30天学java》笔记(P104~P129)
- 什么是GC,为什么需要GC?
- 2017下半年掘金日报优质文章合集:iOS篇
- MATLAB: 2018a百度云资源、迅雷资源、安装步骤
- XGBoost(极端梯度提升)算法原理小结
- 元素在锚点定位后始终在页面居中显示的方法
- PM1200手持无线电综合测试仪的功能与特点