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

333 lines
12 KiB
C++

/********************************************************************
创建日期:2014/04/14
文 件 名:PDSFormula.h
原始作者:孙宇飞
描 述:本文件中的函数用来解析CString类型的公式,以链表形式保存,具体使用方法是:
(1)声明一个CPDSFormula对象,按照CPDSFormula::SetFormula中所列的参数设置,如果不调用该接口,需要手动将各变量设置完整
(2)设置完成后调用CPDSFormula::AnalyseString完成对字符串的解析,结果被保存在m_listFormulaItem中,并且指明了哪些数字项表示长度,哪些表示常量,
对于长度项,m_dConstNumber保存的就是内部长度值,对于常量项,m_dConstNumber保存的就是实际值,
特别说明,对于[-胸围/4]这样的公式将会被解析为[0-胸围/4],
在该步骤中将会形成m_listCalculateFormula链表,它记录了如何将公式计算成数值
(3)计算某号型实际值时,要遍历m_listCalculateFormula链表,将该号型的人体尺寸长度/常量设置到m_dConstNumber中,再调用CalculateResult,其返回值就是结果
注释:
(1)主体采用PDS中解析公式的算法,仅对类名称/成员变量/函数名/宏定义进行一些改动
(2)语义分析函数CPDSFormula::SemanticAnalysis是本次新增,它将会指定哪些数字项是内部长度,哪些数字项是常量
算法:假设公式中含有5个数字项,首先假设这5项均为常量,检查各项是否与m_iFormulaType保持一致,如果不一致,假设有4项为常量,共分5种情况,再检查哪一种情况下的公式与m_iFormulaType保持一致,如此再计算3项2项1项0项常量的情况
(3)公式中区分m_listLengthVariableName与m_listAngleVariableName,前者代表长度,后者代表角度,即常量,设置时一定要将不同类型的人体尺寸/尺寸变量字符串写入到相应的链表中
(4)所有的成员变量都是public类型,方便获取数据,要按照上述的使用方法进行操作,没有列出的函数/变量不可设置
修改记录:
版本号 修改日期 作者 修改内容
*********************************************************************/
#ifndef PDSFORMULA_H
#define PDSFORMULA_H
#include <afxwin.h>
#include <afxtempl.h>
//将函数操作符作为单目运算符,运算级别在括号之后
#define PDS_F_OT_NULL 0x00000000 //
#define PDS_F_OT_ADD 0x00000001 //+ 加
#define PDS_F_OT_SUB 0x00000002 //- 减
#define PDS_F_OT_MULT 0x00000004 //* 乘
#define PDS_F_OT_DIV 0x00000008 /// 除
#define PDS_F_OT_LBRACKET 0x00000010 //( 左括号
#define PDS_F_OT_RBRACKET 0x00000020 //) 右括号
#define PDS_F_OT_SQR 0x00000040 //√ 开方
//下面的操作符使用字符串表示
#define PDS_F_OT_SIN 0x00000080 //sin
#define PDS_F_OT_COS 0x00000100 //cos
#define PDS_F_OT_TG 0x00000200 //tg
#define PDS_F_OT_CTG 0x00000400 //ctg
#define PDS_F_OT_ARCSIN 0x00000800 //arcsin
#define PDS_F_OT_ARCCOS 0x00001000 //arccos
#define PDS_F_OT_ARCTG 0x00002000 //arctg
#define PDS_F_OT_ARCCTG 0x00004000 //arcctg
#define PDS_FORMOULA_STACK_SIZE 100
#define PDS_FOT_MAX_LENGTH 7 //使用字符串表示的操作符中字符串的最大长度
//公式错误信息
enum PDS_ERROR_INFO
{
NoError =0, //没有错误
StackOverflow =1, //栈溢出
ExpressionError =2, //表达式错误(操作符的操作数个数不正确)
NOLBracket =3, //只有右括号,没有左括号
NORBracket =4, //存在没有与左括号匹配的右括号
DivideZero =5, //除数为0
SqrtError =6, //开方数小于0
ArcsinError =7, //反正弦数据错误(数据必须在[-1,1]之间)
ArccosError =8, //反余弦数据错误(数据必须在[-1,1]之间)
StringError =9 //字符串存在错误
};
//公式项
class CPDSFormulaItem
{
public:
int m_iType; //=1 操作符(默认), =2 引用m_pData1/m_pData2, =3 内部长度, =4 常量
int m_iOperatorType; //操作符在m_iType=1时有效,取值范围PDS_F_OT_NULL~PDS_F_OT_ARCCTG
int m_iUnit; //单位,在角度公式下,该变量为-1,m_bOperator=FALSE时有效,默认-1,详见<<PDSUnit.h>>
int m_iPrecision; //单位精度,用于转换成字符串时使用,默认-1,详见<<PDSUnit.h>>
double m_dConstNumber; //常量数值,既可以作为系数也可以作为常变量,单位为m_iUnit,当单位为英寸分数表示时,将分数转换成小数保存
void* m_pData1; //指针1,可根据实际需要指定
void* m_pData2; //指针2
CString m_strText; //字符串,可根据实际需要指定
int m_iIndex; //记录this在链表中的索引值
int m_iPower; //记录this的幂次,若为长度,该值为1,否则为0
public:
CPDSFormulaItem(void);
CPDSFormulaItem(CPDSFormulaItem& rhs);
~CPDSFormulaItem(void);
CPDSFormulaItem& operator=(CPDSFormulaItem& rhs);
BOOL operator==(CPDSFormulaItem& rhs);
BOOL operator!=(CPDSFormulaItem& rhs);
void Init(void);
};
typedef CList<CPDSFormulaItem,CPDSFormulaItem&> CPDSFormulaItemList;
//分析字符串时使用到的类
class CPDSMeasurePos
{
public:
CString m_strMeasureName; //在公式中人体尺寸/尺寸变量的名称
int m_iMeasureIndex; //名称在公式中开始的位置
public:
CPDSMeasurePos(void);
CPDSMeasurePos(CPDSMeasurePos& rhs);
~CPDSMeasurePos(void);
CPDSMeasurePos& operator=(CPDSMeasurePos& rhs);
BOOL operator==(CPDSMeasurePos& rhs);
};
typedef CList<CPDSMeasurePos,CPDSMeasurePos&> CPDSMeasurePosList;
class CPDSFormula
{
public:
BOOL m_bOldFormoula; //=TRUE 旧公式, =FALSE 新公式
int m_iFormulaType; //公式类型, =1 长度公式(默认), =2 角度公式
int m_iDPMM; //长度数据单位:dpmm(每毫米点),用来将字符串计算成内部单位
int m_iUnit; //创建公式时,公式所属长度单位
int m_iPrecision; //与单位对应的长度精确度
CString m_strFormula; //用户输入的公式字符串
int m_iMantissa; //旧公式中尾数对应的系统单位值,目的是为了防止单位转换时出现数据误差,只在旧公式中有效
CStringList m_listLengthVariableName; //表示长度的变量名称
CStringList m_listAngleVariableName; //表示角度的变量名称
CPDSFormulaItemList m_listFormulaItem; //记录通过字符串得到的公式项链(公式项在链表中的顺序与公式字符串一致)
CPDSFormulaItemList m_listCalculateFormula; //存放公式项计算的顺序,用于计算结果
PDS_ERROR_INFO m_ErrorInfo; //公式错误信息
//分析过程中使用的变量
WORD m_iPositionIndex; //分析字符串过程中,指向字符串的当前位置
CPDSFormulaItem m_CurrFormulaItem; //当前分析到的单词
CPDSMeasurePosList m_listMeasurePos; //分析字符串时,字符串中的人体尺寸和尺寸变量名称及其在字符串中位置
//两个栈变量只用于分析字符串的过程
CPDSFormulaItem m_arrFIStack1[PDS_FORMOULA_STACK_SIZE]; //分析过程中存放操作符
int m_iStackTop1; //0..ParserStackSize;当前栈顶位置
CPDSFormulaItem m_arrFIStack2[2 * PDS_FORMOULA_STACK_SIZE]; //分析过程中存放操作数和操作符
int m_iStackTop2; //0..2 * ParserStackSize;当前栈顶位置
public:
CPDSFormula(void);
CPDSFormula(CString strFormula, int iDPMM, int iUnit, int iPrecision, BOOL bOldFormoula, int iFormulaType, CStringList& listLengthVariableName, CStringList& listAngleVariableName);
~CPDSFormula(void);
void Init(void);
//设置函数
void SetFormula(CString strFormula, int iDPMM, int iUnit, int iPrecision, BOOL bOldFormoula, int iFormulaType, CStringList& listLengthVariableName, CStringList& listAngleVariableName);
//判断公式是否正确
//返回值:
// =TRUE 公式正确,=FALSE 公式错误
//说明:
// 判断公式中包含的长度尺寸/角度尺寸是否可以在给定的链表中找到
BOOL IsFormulaRight(void);
//设置变量名称链表
void SetVariableName(CStringList& listLengthVariableName, CStringList& listAngleVariableName);
//将输入的字符串分析成一个一个单词
void AnalyseString(void);
//设置计算链m_listCalculateFormula
void SetCalculateList(void);
//公式项入栈
void Push1(CPDSFormulaItem Item);
//公式项出栈
void Pop1(CPDSFormulaItem& Item);
//公式项入栈
void Push2(CPDSFormulaItem Item);
//判断给定的两个公式项中操作符的优先级
// [输入]Item1,Item2两个公式项
//返回值:
// =0 Item1优先级高于Item2; =1 Item1优先级等于Item2; =2 Item1优先级低于Item2
//说明:
// 该函数中不对公式项是否为操作符进行判断
int JudgePRI(CPDSFormulaItem Item1, CPDSFormulaItem Item2);
//判断给定的公式项中操作符是单目运算还是双目运算()
// [输入] Item 公式项
//返回值:
// =TRUE Item为双目运算符; =FALSE Item单目运算符,即函数运算符
BOOL IsDualOperator(CPDSFormulaItem Item);
//从字符串中得到每个公式项
//返回值:
// =TRUE 表示字符串正确,=FALSE 表示字符串存在错误
// 公式项存放在m_listFormulaItem中
//说明:
//思路:
//1. 先找出字符串中所有的人体尺寸和尺寸变量,并记录下人体尺寸/尺寸变量名称和在字符串中的位置;
//2. 在分析字符串时,先通过位置索引在m_listMeasurePos寻找,到直接返回,找不到通过字符串一个一个分析
//3. 保证第一个公式项为数,并且操作数和操作符(加减乘除)交替
BOOL GetStringFormulaItem(void);
//找出m_strFormula中包含的尺寸名称,将结果输出到listMeasurePos中
void GetMeasureInfo(CPDSMeasurePosList& listMeasurePos);
//得到字符串中的单个单词,即变量名,操作符,数据
//返回值:
// 0 字符串错误,即不是变量名也不是操作符
// 1 到了字符串结尾
// 2 碰到错误字符如:!
// 3 返回值为常量
// 4 返回值为变量
// 5 返回值为符号操作符
// 6 返回值为字符串操作符
//说明:
// 变量名不能是数字开头
// 只有在单位为英制分数的情况下,才进行分数的分析
int NextFormulaItem(void);
//根据索引在m_listMeasurePos中查找对应的MeasurePos
//参数:
// iIndex 索引
// MeasurePos [输出]找到对应的变量位置,返回值为TRUE时有效
//返回值:
// =TRUE 找到, =FALSE 没有找到
//说明:
// 如果找到将找到的信息从m_listMeasurePos移出
// 修改如果同一个位置存在多个名称,取名称最长的
// 人体尺寸/尺寸变量名称存在数字
BOOL FindMeasureIndex(int iIndex, CPDSMeasurePos& MeasurePos);
//判断是否为函数:sin, cos ,tg, ctg, arcsin, arccos, arctg, arcctg
//参数:
// strOperatorName [输出]操作符名称
// iOperatorType [输出]操作符类型
//返回值:
// =TRUE iOperatorType有意义, =FALSE iOperatorType不能使用
BOOL IsFunc(CString &strOperatorName, int &iOperatorType);
//判断是否为变量
//参数:
// strVarName [输出]变量名
//返回值:
// =TRUE strVarName有意义,=FALSE strVarName不能使用
BOOL IsVar(CString &strVarName);
//判断下一个字符是否为操作符
//参数:
// strOperatorName [输出]操作符名称
// iOperatorType [输出]操作符类型
//返回值:
// =TRUE iOperatorType有意义, =FALSE iOperatorType不能使用
//说明:
// 开方在该函数中判断,开方表述方式使用的是符号√
BOOL IsOperator(CString& strOperatorName, int& iOperatorType);
//查找给定的名称是否为人体尺寸
//返回值:
// =TRUE 找到变量, =FALSE 没有找到变量
BOOL FindVariableForName(CString strVarName);
//语义分析
//说明:
// 对解析后的m_listFormulaItem进行语义分析,确定其中哪些数字表示内部单位,哪些表示常量
void SemanticAnalysis(void);
//设置幂次
//参数:
// listFormulaItem 公式项链表
// iPower 幂次,=0 表示常量, =1 表示长度, = 2表示长度*长度,以此类推
void SetPower(CPDSFormulaItemList& listFormulaItem, int iPower);
//检查给定链表的幂次是否等于给定值
//参数:
// listFormulaItem 链表
// iPower 幂次, =0 表示常量, =1 表示长度, =2 表示长度*长度,以此类推
//返回值:
// =TRUE 正确, =FALSE 错误
BOOL CheckPower(CPDSFormulaItemList& listFormulaItem, int iPower);
//判断给定链表中是否含有括号
//参数:
// listFormulaItem 公式链表
// iIndexL [输出]最左边的括号在链表中的索引值
// iIndexR [输出]与iIndexL对应的右括号在链表中的索引值
//返回值:
// =TRUE 有括号,(iIndexL,iIndexR)数据有效, =FALSE 没有括号,输出变量无效
BOOL HaveBracket(CPDSFormulaItemList& listFormulaItem, int& iIndexL, int& iIndexR);
//排列组合
//参数:
// iMaxCount 总数
// iCount 被提取出的数量
// alIndex [输出]排列组合链表,其中的alIndex[].GetCount()=iCount,索引值从0开始,从大到小排列
void Permutation(int iMaxCount, int iCount, CArray<CList<int,int>,CList<int,int> >& alIndex);
//获取给定链表的幂次,如果计算失败则返回INT_MAX
//特别说明:什么是计算失败?
// 例如:A*B+C由加号连接的每一项幂次不等,sqrt(A+B)用来开方的数据不是常数或者2的整数倍,sin(A)三角函数的参数不是常量,等等
//返回值:
// 幂次,=0 表示常量, =1 表示长度, = 2表示长度*长度,以此类推
int GetPower(CPDSFormulaItemList& listFormulaItem);
//计算结果
//特别说明:
// 调用本函数之前,要将设置m_listFormulaItem链表,将人体尺寸的实际值设置到m_dConstNumber中,切记!!!
//返回值:
// 如果this是长度公式,那么返回值表示长度,四舍五入后可即为内部长度,如果this是角度公式,那么返回值就是角度
double CalculateResult(void);
//计算旧公式
double CalculateOldFormula(void);
//计算新公式
double CalculateNewFormula(void);
//双目操作符运算
//参数:
// iOperator 操作符
// dValue1 第一操作数
// dValue2 第二操作数
//返回值:
// 结果项(数据公式项)
CPDSFormulaItem CalculateValue1(int iOperator, double dValue1, double dValue2);
//单目操作符运算
//参数:
// iOperator 操作符
// dValue1 第一操作数
//返回值:
// 结果项(数据公式项)
CPDSFormulaItem CalculateValue2(int iOperator, double dValue1);
};
#endif // PDSFORMULA_H