PlotterHMI/datafile/dxf/dxflib/PDSMath/PDSNurbs.h
huahaiyan 9f74511e69 20240315
1、增加数据分割功能(完成)
2、更换dxf库(进行中)
2024-03-15 16:49:50 +08:00

503 lines
18 KiB
C++

/********************************************************************
创建日期:2017年3月17日
文 件 名:PDSNurbs.h
原始作者:孙宇飞
描 述:非均匀有理B样条
修改记录:
版本号 修改日期 作者 修改内容
*********************************************************************/
#ifndef _PDS_NURBS_H_
#define _PDS_NURBS_H_
#include <math.h>
#include <afxwin.h>
#include <afxtempl.h>
const double PDS_NURBS_ZERO = 0.000001; // 小于该数字的值认为等于0
//全局函数
//判断两个双精度数是否相等
BOOL PDSNurbs_IsEqual(double d1, double d2);
//判断两个双精度数是否不等
BOOL PDSNurbs_NotEqual(double d1, double d2);
//判断给定的双精度数是否等于0
BOOL PDSNurbs_EqualZero(double d);
//判断给定的双精度数是否不为0
BOOL PDSNurbs_NotEqualZero(double d);
//计算非均匀有理B样条使用的点
class CPDSNurbsPoint : public CObject
{
public:
double x;
double y;
double z;
double weight; /* 权重,默认值:1
// 举例:(1,1,1,1)等同于(4,4,4,4),当为某一个点设置权重的时候,一定要将xyz分量全都除以weight才能保证位置不变 */
double knot; // 该点对应的节点数值,默认值:0
int type; // 类型,=1 转折点,=0 曲线点,默认值:0,[20180322 syf]增加该变量
// 当this表示控制点时,启用该变量,那么在反求样条曲线的控制点链时,从转折点出断开,该变量不参与等号operator==的判断
public:
CPDSNurbsPoint(void);
CPDSNurbsPoint(CPDSNurbsPoint& rhs);
CPDSNurbsPoint(CPoint ptPoint);
CPDSNurbsPoint(double x, double y, double z = 0.0, double weight = 1.0);
virtual ~CPDSNurbsPoint(void);
CPDSNurbsPoint& operator=(CPDSNurbsPoint& rhs);
CPDSNurbsPoint operator+(CPDSNurbsPoint& rhs);
CPDSNurbsPoint operator-(CPDSNurbsPoint& rhs);
void operator+=(CPDSNurbsPoint& rhs);
BOOL operator==(CPDSNurbsPoint& rhs);
void Initial(void);
//将this的坐标转换为CPoint并返回
CPoint GetPoint(void);
//设置this的坐标
void SetPoint(CPoint ptPoint);
//乘以给定的数值
void Multiple(double dParam);
//除以给定的数值
void Divide(double dParam);
//将每个分量除以权重,得到规范后的坐标
void DivideWeight(void);
};
//带类型的点的链表
class CPDSNurbsPointList : public CList<CPDSNurbsPoint,CPDSNurbsPoint>
{
public: //扩展的变量
public: //扩展的方法
CPDSNurbsPointList& operator=(CPDSNurbsPointList& rhs); //赋值
CPDSNurbsPoint& operator[](int iIndex); //根据索引值检索,不可越界
//根据listPoint设置this
//特别说明:
// (1)this中每个点z=0,weight=1
void SetPoint(CList<CPoint,CPoint>& listPoint);
//将this的数据输出至listPoint
void GetPoint(CList<CPoint,CPoint>& listPoint);
POSITION AddWithoutDuplicate(CPDSNurbsPoint ptNurbsPoint, BOOL bAddTail = TRUE);
void AddWithoutDuplicate(CPDSNurbsPointList& listNurbsPoint, BOOL bAddTail = TRUE);
//根据给定的节点数值,查找索引值
int FindKnotIndex(double dKnot);
//获取长度
double GetLength(void);
//将this翻转
void Reverse(void);
};
//节点链表
//对于一条NURBS曲线来说(3次),该链表的长度应该等于控制点链长度+4
class CPDSNurbsKnotList : public CList<double,double>
{
public: //扩展的变量
public: //扩展的方法
CPDSNurbsKnotList& operator=(CPDSNurbsKnotList& rhs); //赋值
double& operator[](int iIndex); //根据索引值检索,不可越界
//根据listKnot设置this
void SetKnot(CList<double,double>& listKnot);
//将this的数据输出至listKnot
void GetKnot(CList<double,double>& listKnot);
//添加新的节点,并且相邻的节点不能相同
POSITION AddWithoutDuplicate(double dKnot, BOOL bAddTail = TRUE);
//查找给定的节点在this中的重复次数
int FindRepeatTime(double dKnot);
};
//矩阵
class CPDSMatrix : public CObject
{
protected:
double** m_dMatrix; // 二维数组的指针,为this私有,不同的对象之间不能共享m_dMatrix
int m_iRowCount; // 行数
int m_iColumnCount; // 列数
public:
CPDSMatrix(void);
CPDSMatrix(CPDSMatrix& rhs);
virtual ~CPDSMatrix(void);
CPDSMatrix& operator=(CPDSMatrix& rhs);
CPDSMatrix operator*(CPDSMatrix& rhs); // this的列数必须等于rhs的行数,否则不予计算
//初始化,用于释放内存
void Initial(void);
//设置矩阵的大小
//特别说明:
// 在使用矩阵的数据之前,一定要首先设置大小
void SetSize(int iRowCount, int iColumnCount);
//获取矩阵的数据
void GetMatrix(double**& dMatrix, int& iRowCount, int& iColumnCount);
//获取二维数组的指针
double** GetArray(void);
//获取逆矩阵
//特别说明:
// (1)this必须是方阵
// (2)本函数根据初等变换计算
CPDSMatrix GetInverseMatrix(void);
//输出调试信息
void OutputDebugInfo(void);
//将两行的数据加起来
//输入参数:
// dArray1 数组1,它的数据被增加
// dArray2 数组2,其中的数据被加到dArray1,自身的数据不改变
// iCount 两个数组的长度
// dProp 比例值,dArray2[]*dProp被增加到dArray1
void AddRow(double* dArray1, double* dArray2, int iCount, double dProp = 1.0);
//将数组中的每一项除以指定的参数
//输入参数:
// dArray 数组
// iCount 数组的长度
// dParam 参数,数组的每一项都要除以该值
void DivideRow(double* dArray, int iCount, double dParam);
};
//NURBS曲线类
//特别说明:
// (1)特指3次非均匀有理B样条曲线
// (2)什么是型值点?
// 指的是经过的点,在我们的系统中称之为"控制点",实际上规范化的定义叫做型值点
// 型值点也叫做拟合点
// 英文名:data point
// (3)什么是控制点?
// 用于计算实际点链的点,部分版师形象的称之为"手臂",曲线只会经过第一点与最后一点,不会经过中间的其他点
// 控制点链,也叫做特征多边形,它是非均匀有理B样条的凸包,曲线不会超过它的范围
// 英文名:control point
// (4)NURBS曲线的方程由递推公式给出,详见相关文档
// (5)什么是节点?
// 一组单调不减的数字,标识每一个control point的作用范围,对于相同的特征多边形来说,选择不同的节点,最终的效果不同
// (6)节点有多少个?
// 对于degree=3来说,节点个数=特征多边形个数+degree+1=特征多边形个数+4
// 并且前degree+1=4个节点相等(未必等于0),末尾degree+1=4个节点相等(未必等于1)
// 例如:{A,A,A,A,B,C,D,E,E,E,E}就是7个control point所形成的节点,每一个{A,B,C,D,E}都对应于一个data point,而这个data point在屏幕上可以看到的点
// 如果{A,B,C,D,E}构成等差数列,曲线叫做均匀有理B样条,否则叫做非均匀有理B样条
// (7)如果只有3个控制点,那么只能计算2次曲线
// (8)如果只有2个控制点,那么只能得到一条线段
class CPDSNurbsCurve : public CObject
{
public:
//将4条链表封装在一起,方便写代码,NURBS曲线的计算函数并不使用成员变量
CPDSNurbsPointList m_listCtrlPoint; // 控制点链,也叫特征多边形,最终的实际点链只经过头尾点,而不经过中间的其他点
CPDSNurbsKnotList m_listKnot; // 节点链表,长度等于m_listCtrlPoint.GetCount()+4
CPDSNurbsPointList m_listDataPoint; // 型值点链,经过的那些点
CPDSNurbsPointList m_listRealPoint; // 实际点链,插值后的点
public:
CPDSNurbsCurve(void);
CPDSNurbsCurve(CPDSNurbsCurve& rhs);
virtual ~CPDSNurbsCurve(void);
CPDSNurbsCurve& operator=(CPDSNurbsCurve& rhs);
void Initial(void);
//根据型值点计算开口线
//输入参数:
// listDataPoint 型值点
// dMaxError 最大误差值
// bTangentH =true 头点有切线,=false 头点没有切线
// ptTangentPointH 头点的切线方向,指的是实际点链GetPrev的方向
// bTangentT =true 尾点有切线,=false 尾点没有切线
// ptTangentPointT 尾点的切线方向,指的是实际点链GetNext的方向
//输出参数:
// listOutputPoint 实际点链
//返回值:
// =true 计算成功,=false 计算失败
BOOL Calculate_DataPoint_Open(CList<CPoint,CPoint>& listDataPoint, double dMaxError, CList<CPoint,CPoint>& listOutputPoint, BOOL bTangentH = FALSE, CPoint ptTangentPointH = CPoint(0,0), BOOL bTangentT = FALSE, CPoint ptTangentPointT = CPoint(0,0));
//根据型值点和切线点计算闭合线
//输入参数:
// listDataPoint 型值点,尾点可以等于头点,也可以不等,本函数自动处理
// dMaxError 最大误差值
//输出参数:
// listOutputPoint 实际点链,输出时尾点与头点重合
//返回值:
// =true 计算成功,=false 计算失败
BOOL Calculate_DataPoint_Close(CList<CPoint,CPoint>& listDataPoint, double dMaxError, CList<CPoint,CPoint>& listOutputPoint);
//根据实际点链反求型值点
//输入参数:
// listOldPoint 旧的实际点链
// listDataPoint 型值点,如果输入时不为空,表示必须经过这些点
// dSplineError 计算样条曲线的最大误差值
// dMaxError 反求之后的曲线,与listOldPoint的最大误差值
// bTangentH =true 头点有切线,=false 头点没有切线
// ptTangentPointH 头点的切线方向,指的是实际点链GetPrev的方向
// bTangentT =true 尾点有切线,=false 尾点没有切线
// ptTangentPointT 尾点的切线方向,指的是实际点链GetNext的方向
//输出参数:
// listDataPoint 型值点,如果输入时不为空,表示必须经过这些点
//返回值:
// =true 计算成功,=false 计算失败
BOOL ReCalculate_DataPoint(
CList<CPoint,CPoint>& listOldPoint, CList<CPoint,CPoint>& listDataPoint, double dSplineError, double dMaxError,
BOOL bTangentH = FALSE, CPoint ptTangentPointH = CPoint(0,0), BOOL bTangentT = FALSE, CPoint ptTangentPointT = CPoint(0,0));
//根据给定的控制点链与节点链计算实际点链
//特别说明:
// (1)本函数将会自动设置m_listCtrlPoint与m_listKnot,无需事先设置
// (2)输入时要保证listKnot.GetCount()=listCtrlPoint.GetCount()+4,这是NURBS曲线的要求,并且要保证节点数的正确性:单调不减,否则计算的结果就是错误的
// (3)从DXF等文件中读入的NURBS曲线,可以直接调用本函数来计算实际点链
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// dMaxError 最大误差值
//输出参数:
// listDataPoint 型值点链,即:曲线经过的那些点
// listRealPoint 实际点链
//返回值:
// =true 计算成功,=false 计算失败
BOOL Calculate_CtrlPoint(CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, double dMaxError, CPDSNurbsPointList& listDataPoint, CPDSNurbsPointList& listRealPoint);
//根据给定的控制点链与节点链计算型值点
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
//输出参数:
// listDataPoint 型值点链,即:曲线经过的那些点
//返回值:
// =true 计算成功,=false 计算失败
BOOL Calculate_CtrlPoint(CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, CPDSNurbsPointList& listDataPoint);
protected:
//根据给定的型值点计算控制点(计算开口线)
//输入参数:
// listDataPoint 型值点链,指的是曲线经过的那些点
// bTangentH =true 头点有切线,=false 头点没有切线
// ptTangentPointH 头点的切线方向,指的是实际点链GetPrev的方向
// bTangentT =true 尾点有切线,=false 尾点没有切线
// ptTangentPointT 尾点的切线方向,指的是实际点链GetNext的方向
//输出参数:
// listCtrlPoint 控制点链,它是NURBS曲线的控制多边形或者说特征多边形,其长度=listDataPoint.GetCount()+2
// listKnot 节点链表
void CalculateCtrlPoint_Open(CPDSNurbsPointList& listDataPoint, BOOL bTangentH, CPDSNurbsPoint ptTangentPointH, BOOL bTangentT, CPDSNurbsPoint ptTangentPointT, CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot);
//根据给定的型值点计算控制点(闭合线)
//特别说明:
// (1)listInputPoint的尾点可以等于头点,也可以不等于
// (2)最终输出的listCtrlPoint头尾点相等,表示再次回到头点上
//输入参数:
// listInputPoint 型值点链,指的是曲线经过的那些点
//输出参数:
// listCtrlPoint 控制点链,它是NURBS曲线的控制多边形或者说特征多边形
// listKnot 节点链表
void CalculateCtrlPoint_Close(CPDSNurbsPointList& listInputPoint, CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot);
//获取给定线段的长度
//输入参数:
// listCtrlPoint 控制点链
// iIndex 段的索引值,指的是(listCtrlPoint[iIndex], [iIndex+1])所形成的线段,例如:第0段指的是([0],[1])
//返回值:
// 长度,如果传入的索引值越界则返回0
double GetSegmentLength(CPDSNurbsPointList& listCtrlPoint, int iIndex);
//查找给定节点的索引值
//特别说明:
// (1)如果给定的节点位于[listKnot[i],listKnot[i+1]),那么返回i
// (2)注意可以等于listKnot[i],一定要严格小于[i+1]
// (3)从NURBS曲线的定义可以知道,由于{A,A,A,A...}前四个数据重复,所以该函数的返回值从3开始
//输入参数:
// listKnot 节点链表,单调不减
// dKnot 节点
//返回值:
// 索引值,从0开始,若为-1则表示查找失败
int FindKnotIndex(CPDSNurbsKnotList& listKnot, double dKnot);
//根据给定的节点数值,计算B样条基函数的值
//特别说明:
// (1)这是一个递归函数,需要调用iDegree-1的数据进行计算
// (2)在计算的过程中可能出现分母为0/0的情况,此处我们定义0/0=0
//输入参数:
// listKnot 节点链表
// dKnot 给定的节点数值
// iIndex dKnot所在的索引值
// iDegree NURBS曲线的次数,一般等于3,如果只有三个控制点,那么只能生成2次曲线
//返回值:
// B样条基函数的值
double CalculateKnot(CPDSNurbsKnotList& listKnot, double dKnot, int iIndex, int iDegree);
//根据给定的节点数值,计算NURBS曲线上的点坐标
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// dKnot 给定的节点数值
// iDegree 样条曲线的次数
//返回值:
// 计算出的点位置
CPDSNurbsPoint CalculateNurbsPoint(CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, double dKnot, int iDegree);
//根据给定的两个节点,计算弦高
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// ptPrevNurbsPoint 前一个点的坐标
// dPrevKnot 前一个点的节点
// ptNextNurbsPoint 后一个点的坐标
// dNextKnot 后一个点的节点
// iDegree 样条曲线的次数
//返回值:
// 弦高
double CalculateChordHeight(
CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot,
CPDSNurbsPoint ptPrevNurbsPoint, double dPrevKnot,
CPDSNurbsPoint ptNextNurbsPoint, double dNextKnot, int iDegree);
//根据给定的数量计算等分后的节点
//特别说明:
// (1)若dPrevKnot=dNextKnot,那么输出链表为空
//输入参数:
// dPrevKnot 前一个节点
// dNextKnot 后一个节点
// iCount 给定节点之间的点数量,若=2,表示有2个点,输出链表为{dPrevKnot,节点1,节点2,dNextKnot}
//输入参数:
// listDivideKnot 分割后的节点链表,包括dPrevKnot与dNextKnot
void CalculateDivideKnot(double dPrevKnot, double dNextKnot, int iCount, CPDSNurbsKnotList& listDivideKnot);
//计算两个节点之间的实际点链
//特别说明:
// (1)若dPrevKnot=dNextKnot,那么输出链表只有一个点
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// dPrevKnot 前一个节点
// dNextKnot 后一个节点
// iDegree 样条曲线的次数
// dMaxError 最大误差值,即:每条线段的弦高不能超过该值
//输出参数:
// listOutputPoint 实际点链,包含dPrevKnot与dNextKnot对应的点
void CalculateNurbsPoint_BetweenTwoKnot(
CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, double dPrevKnot, double dNextKnot,
int iDegree, double dMaxError, CPDSNurbsPointList& listOutputPoint);
//根据等分后的节点链表计算实际点链
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// listDivideKnot 等分后的节点链表
// iDegree 样条曲线的次数
// dMaxError 最大误差值,即:每条线段的弦高不能超过该值
//输出参数:
// listOutputPoint 实际点链,如果计算成功,则不为空,如果计算失败,该链表为空
void CalculateNurbsPoint_UseDivideKnot(
CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, CPDSNurbsKnotList& listDivideKnot,
int iDegree, double dMaxError, CPDSNurbsPointList& listOutputPoint);
//计算中点
//输入参数:
// ptPrevNurbsPoint 前一个点,将其weight去掉之后再求解
// ptNextNurbsPoint 后一个点,将其weight去掉之后再求解
//返回值:
// 中点,它的weight=1
CPDSNurbsPoint GetMiddleNurbsPoint(CPDSNurbsPoint ptPrevNurbsPoint, CPDSNurbsPoint ptNextNurbsPoint);
//计算两个点之间的距离
//输入参数:
// ptPrevNurbsPoint 前一个点,将其weight去掉之后再求解
// ptNextNurbsPoint 后一个点,将其weight去掉之后再求解
//返回值:
// 距离
double TwoNurbsPointLength(CPDSNurbsPoint ptPrevNurbsPoint, CPDSNurbsPoint ptNextNurbsPoint);
//根据给定点在线段上的参数,计算该点
CPDSNurbsPoint ParamToNurbsPoint(CPDSNurbsPoint ptPrevNurbsPoint, CPDSNurbsPoint ptNextNurbsPoint, double dParam);
//查找链表中的哪个点与给定点最近,返回其索引值
//输入参数:
// listPoint 链表
// ptSelPoint 给定点
//输出参数:
// dMinDistance 最近距离
//返回值:
// 索引值,从0开始
int FindNearestIndex(CList<CPoint,CPoint>& listPoint, CPoint ptSelPoint, double& dMinDistance);
//以累加弦长的方式生成节点
//输入参数:
// listDataPoint 型值点,至少3个点
//输出参数:
// listUniqueKnot 不重复的节点链表
// listKnot 重复的节点链表
void GenerateKnot_ChordLength(CPDSNurbsPointList& listDataPoint, CPDSNurbsKnotList& listUniqueKnot, CPDSNurbsKnotList& listKnot);
//计算垂足在矢量上的参数值
//输入参数:
// ptNurbsPointS 矢量S
// ptNurbsPointE 矢量E
// ptNurbsPoint1 给定点
//返回值:
// 垂足点在矢量上的参数,如果SE点重合则直接返回0
double GetNurbsPointVerticalParam(CPDSNurbsPoint ptNurbsPointS, CPDSNurbsPoint ptNurbsPointE, CPDSNurbsPoint ptNurbsPoint1);
//获取给定点到链表的距离
//输入参数:
// ptNurbsPoint1 给定点
// listNurbsPoint 链表
//返回值:
// 距离
double DistanceOfNurbsPointPloyline(CPDSNurbsPoint ptNurbsPoint1, CPDSNurbsPointList& listNurbsPoint);
//查找链表中的哪个点与给定点最近
//输入参数:
// ptNurbsPoint1 给定点
// listNurbsPoint 链表
//输出参数:
// dDistance 最近距离
//返回值:
// 最近点
CPDSNurbsPoint FindNearestNurbsPoint(CPDSNurbsPoint ptNurbsPoint1, CPDSNurbsPointList& listNurbsPoint, double& dDistance);
//判断新链表与旧链表的距离是否小于误差值
//输入参数:
// listOldPoint 旧的链表
// listNewPoint 新的链表
// dMaxError 最大误差值
//返回值:
// =true 新链表的每个点,到旧链表的距离都小于或者等于误差值,=false 某个点的距离大于误差值
BOOL DistanceSmallerThanError(CList<CPoint,CPoint>& listOldPoint, CList<CPoint,CPoint>& listNewPoint, double dMaxError);
//根据给定的控制点链与节点链计算实际点链,并且实际点链与旧点链的距离小于误差
//输入参数:
// listCtrlPoint 控制点链
// listKnot 节点链表
// iSelIndex 给定的索引值,从0开始,从该索引值开始,向前和向后逐段计算,并比较偏差,该索引值附近的偏差是最大的
// dSplineError 计算NURBS曲线的最大误差值
// dMaxError 实际点链与旧点链的最大误差值
// listOldPoint 旧点链
//返回值:
// =true 计算成功,每个点都小于等于误差值,=false 计算失败,某个点的距离大于误差值
BOOL Calculate_CtrlPoint_SmallerThanError(CPDSNurbsPointList& listCtrlPoint, CPDSNurbsKnotList& listKnot, int iSelIndex, double dSplineError, double dMaxError, CList<CPoint,CPoint>& listOldPoint);
};
#endif // _PDS_NURBS_H_