结合 .h 和 .cpp 文件,以下是 RS_Units 类的完整解析。
概述
RS_Units 类是 LibreCAD 中处理单位转换和格式化的核心静态类,包含全面的单位系统支持、格式转换和数值处理功能。
文件结构
头文件 (rs_units.h)
cpp
#ifndef RS_UNITS_H
#define RS_UNITS_H
#include "rs.h"
class RS_Vector;
class QString;
/**
* 单位转换方法
*/
class RS_Units {
private:
static RS2::Unit currentDrawingUnits;
public:
// 单位管理方法
static void setCurrentDrawingUnits(RS2::Unit input_units);
static RS2::Unit getCurrentDrawingUnits();
// 单位转换方法
static RS2::Unit dxfint2unit(int dxfint);
static QString unitToString(RS2::Unit u, bool t = true);
static RS2::Unit stringToUnit(const QString& u);
static bool isMetric(RS2::Unit u);
static double getFactorToMM(RS2::Unit u);
static double convert(double val);
static double convert(double val, RS2::Unit src, RS2::Unit dest);
static RS_Vector convert(const RS_Vector& val, RS2::Unit src, RS2::Unit dest);
// 格式化方法
static QString unitToSign(RS2::Unit u);
static QString formatLinear(double length, RS2::Unit unit,
RS2::LinearFormat format,
int prec, bool showUnit=false);
static QString formatScientific(double length, RS2::Unit unit,
int prec, bool showUnit=false);
static QString formatDecimal(double length, RS2::Unit unit,
int prec, bool showUnit=false);
static QString formatEngineering(double length, RS2::Unit unit,
int prec, bool showUnit=false);
static QString formatArchitectural(double length, RS2::Unit unit,
int prec, bool showUnit=false);
static QString formatFractional(double length, RS2::Unit unit,
int prec, bool showUnit=false);
static QString formatArchitecturalMetric(double length, RS2::Unit unit,
int prec, bool showUnit=false);
// 角度处理方法
static QString formatAngle(double angle, RS2::AngleFormat format, int prec);
static RS2::AngleFormat numberToAngleFormat(int num);
// 图纸处理方法
static RS_Vector paperFormatToSize(RS2::PaperFormat p);
static RS2::PaperFormat paperSizeToFormat(const RS_Vector& s);
static QString paperFormatToString(RS2::PaperFormat p);
static RS2::PaperFormat stringToPaperFormat(const QString& p);
// DPI转换方法
static double dpiToScale(double dpi, RS2::Unit unit);
static double scaleToDpi(double scale, RS2::Unit unit);
// 角度解析方法
static QString replaceSurveyorsAnglesByDecimalDegrees(QString val, bool *ok, QString& errorMsg);
static QString replaceRadiantAnglesByDecimalDegrees(QString& val, bool *ok);
static QString replaceGradAnglesByDecimalDegrees(QString& val, bool *ok);
static QString replaceExplicitDegreesByDecimalDegrees(QString& val, bool *ok);
static QString replaceExplicitBearingAnglesByDecimalDegrees(QString& val, bool *ok);
static QString replaceAllPotentialAnglesByDecimalDegrees(const QString& val, bool *ok);
static double evalAngleValue(const QString &c, bool *ok);
// 测试方法
static void test();
};
#endif
实现细节分析
1. 单位系统支持
支持的计量单位:
- 英制单位:
Inch、Foot、Mile、Yard、Microinch、Mil - 公制单位:
Millimeter、Centimeter、Meter、Kilometer等 - 天文单位:
Astro、Lightyear、Parsec、Angstrom - 特殊单位:
Micron、Nanometer、Gigameter等
单位转换因子:
cpp
// 示例转换因子(毫米为基准)
static double getFactorToMM(RS2::Unit u) {
switch (u) {
case RS2::Inch: return 25.4; // 1英寸 = 25.4毫米
case RS2::Foot: return 304.8; // 1英尺 = 304.8毫米
case RS2::Mile: return 1.609344e6; // 1英里 = 1,609,344毫米
case RS2::Millimeter: return 1.0; // 基准单位
case RS2::Centimeter: return 10.0; // 1厘米 = 10毫米
case RS2::Meter: return 1000.0; // 1米 = 1000毫米
// ... 其他单位
}
}
2. 格式转换系统
线性格式类型:
Scientific:科学计数法(如 2.5E7)Decimal:十进制格式(如 2.5)Engineering:工程格式(如 5' 4.5")Architectural:建筑格式(如 5' 4 1/2")Fractional:分数格式(如 5 3/64")ArchitecturalMetric:公制建筑格式(DIN 406)
角度格式类型:
DegreesDecimal:十进制度数(如 45.5°)DegreesMinutesSeconds:度分秒(如 45°30'15")Gradians:百分度(如 50g)Radians:弧度(如 0.785r)Surveyors:测量员格式(如 N45d30'E)
3. 核心算法实现
单位转换算法
cpp
double RS_Units::convert(double val, RS2::Unit src, RS2::Unit dest) {
if (getFactorToMM(dest) > 0.0) {
return (val * getFactorToMM(src)) / getFactorToMM(dest);
} else {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_Units::convert: invalid factor");
return val;
}
}
建筑格式转换算法
cpp
QString RS_Units::formatArchitectural(double length, RS2::Unit unit,
int prec, bool showUnit) {
// 验证数值范围
if (!isAbsInRange(length))
return "<Invalid length>";
QString negativeSign = std::signbit(length)?"-":"";
unsigned feet = convert(std::abs(length), unit, RS2::Foot);
double inches = convert(std::abs(length), unit, RS2::Inch) - feet * 12;
// 处理进位
if (inches >= 12.) {
feet++;
inches -= 12.;
}
QString sInches = formatFractional(inches, RS2::Inch, prec, showUnit);
if (feet != 0) {
return QString(R"(%1%2'-%3")").arg(negativeSign).arg(feet).arg(sInches);
} else {
return QString(R"(%1")").arg(sInches);
}
}
分数格式简化算法
cpp
QString RS_Units::formatFractional(double length, RS2::Unit /*unit*/,
int prec, bool /*showUnit*/) {
if (!isAbsInRange(length))
return "<Invalid length>";
// 处理负数
if (std::signbit(length)) {
QString sign = std::signbit(length + RS_TOLERANCE) ? QString{"- "} : QString{};
return sign + formatFractional(std::abs(length), RS2::Inch, prec, false);
}
unsigned num = (unsigned) std::abs(length);
unsigned denominator = 2 << prec; // 2^(prec+1)
unsigned nominator = (unsigned) RS_Math::round((length-num)*denominator);
// 分数进位处理
if (nominator == denominator) {
nominator = 0;
denominator = 0;
++num;
}
// 分数简化(求最大公约数)
if (nominator != 0 && denominator != 0) {
unsigned gcd = RS_Math::findGCD(nominator, denominator);
if (gcd) {
nominator = nominator / gcd;
denominator = denominator / gcd;
}
}
// 构建结果字符串
QString neg = std::signbit(length) ? "-" : "";
if (num != 0 && nominator != 0) {
return QString("%1%2 %3/%4").arg(neg).arg(num).arg(nominator).arg(denominator);
} else if(nominator != 0) {
return QString("%1%2/%3").arg(neg).arg(nominator).arg(denominator);
} else if(num != 0) {
return QString("%1%2").arg(neg).arg(num);
} else {
return "0";
}
}
4. 角度解析系统
使用正则表达式处理各种角度格式:
测量员角度格式解析
cpp
static QRegularExpression surveyorRegExp = QRegularExpression(
"\\b" // 单词边界
"(?:"
"(?:"
"([NS])" // 北或南
"(?:"
"([+-]?)" // 正负号
"(?:"
"(?:(\\d*\\.?\\d*)[d°])?" // 度数(带d或°符号)
"(?:(\\d*\\.?\\d*)')?" // 分钟(带'符号)
"(?:(\\d*\\.?\\d*)\")?" // 秒数(带"符号)
"|" // 或者...
"(\\d*)" // 单纯度数(无符号)
")"
"([EW])" // 东或西
")?"
")"
"|" // 或者...
"([EW])" // 仅东或西(0°或180°)
")"
"\\b",
QRegularExpression::CaseInsensitiveOption
);
角度转换流程
cpp
double RS_Units::evalAngleValue(const QString &c, bool *ok){
// 1. 替换测量员角度为十进制度数
QString normalizedString = replaceAllPotentialAnglesByDecimalDegrees(c, ok);
// 2. 计算数值
double result = 0.0;
if (ok && *ok){
result = RS_Math::eval(normalizedString, ok);
}
return result;
}
5. 图纸格式支持
支持的图纸格式:
- ISO A系列:
A0、A1、A2、A3、A4 - ANSI系列:
Ansi_C、Ansi_D、Ansi_E - Arch系列:
Arch_A、Arch_B、Arch_C、Arch_D、Arch_E - 北美标准:
Letter、Legal、Tabloid
图纸尺寸定义:
cpp
RS_Vector RS_Units::paperFormatToSize(RS2::PaperFormat p) {
switch (p) {
case RS2::A0: return RS_Vector(841.0, 1189.0); // A0尺寸
case RS2::A1: return RS_Vector(594.0, 841.0); // A1尺寸
case RS2::A2: return RS_Vector(420.0, 594.0); // A2尺寸
case RS2::A3: return RS_Vector(297.0, 420.0); // A3尺寸
case RS2::A4: return RS_Vector(210.0, 297.0); // A4尺寸
case RS2::Letter: return RS_Vector(215.9, 279.4); // Letter尺寸
// ... 其他格式
}
}
6. 错误处理机制
数值范围检查:
cpp
namespace {
bool isAbsInRange(double length) {
if (std::isnan(length) || !std::isfinite(length)) {
RS_DEBUG->print(RS_Debug::D_ERROR, "length=%lg", length);
return false;
}
if (std::abs(length) > std::numeric_limits<unsigned>::max()) {
RS_DEBUG->print(RS_Debug::D_ERROR,
"length=%lg is too big in magnitude", length);
return false;
}
return true;
}
}
角度解析错误处理:
cpp
QString RS_Units::replaceSurveyorsAnglesByDecimalDegrees(QString val,
bool *ok,
QString& errorMsg) {
// ... 解析逻辑
if (!group8.isEmpty()) {
if (group8 == "E") {
angle = 0.0;
} else if (group8 == "W") {
angle = 180.0;
} else {
if (ok != nullptr) {
*ok = false;
}
errorMsg = "Unexpected cardinal direction for surveyor angle";
return "";
}
}
// ... 继续解析
}
使用示例
基本单位转换
cpp
// 将10英寸转换为毫米
double mm = RS_Units::convert(10.0, RS2::Inch, RS2::Millimeter);
// 结果:254.0
// 使用当前绘图单位转换
RS_Units::setCurrentDrawingUnits(RS2::Centimeter);
double converted = RS_Units::convert(100.0); // 从毫米转换到厘米
格式显示
cpp
// 显示建筑格式
QString display = RS_Units::formatArchitectural(65.125, RS2::Inch, 3, true);
// 结果:5' 5 1/8"
// 显示分数格式
QString fraction = RS_Units::formatFractional(2.375, RS2::Inch, 3, false);
// 结果:2 3/8
// 显示角度
QString angle = RS_Units::formatAngle(M_PI/4, RS2::DegreesMinutesSeconds, 2);
// 结果:45° 0' 0"
角度解析
cpp
bool ok;
QString errorMsg;
// 解析测量员角度
QString result = RS_Units::replaceSurveyorsAnglesByDecimalDegrees(
"N45d30'E", &ok, errorMsg);
// 结果:45.5
// 解析多种角度格式
double angle = RS_Units::evalAngleValue("45°30' + 0.5r", &ok);
// 结果:75.5(度)
设计特点
- 完整的单位系统:支持从微观(埃)到天文(光年)的广泛单位
- 专业的格式支持:建筑、工程、科学等多种专业格式
- 强大的角度解析:支持测量员、弧度、百分度等特殊角度格式
- 国际化支持:使用Qt的翻译系统,支持多语言
- 错误处理完善:包含NaN检查、数值范围验证和详细的错误信息
- 性能优化:使用静态方法和正则表达式预编译提高效率
应用场景
- CAD尺寸标注:自动根据设置的单位和格式显示尺寸
- 数据导入导出:处理DXF文件中的单位信息
- 用户输入解析:智能解析用户输入的各种格式的角度和长度
- 图纸输出:按不同标准和格式输出图纸尺寸
- 单位换算工具:在英制和公制系统间转换数据
这个类体现了专业CAD软件在计量单位和格式处理方面的深度需求,是LibreCAD能够处理国际标准和专业工程需求的关键组件。