【LibreCAD】 RS_Units 类完整解析

结合 .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. 单位系统支持

支持的计量单位:

  • 英制单位:InchFootMileYardMicroinchMil
  • 公制单位:MillimeterCentimeterMeterKilometer
  • 天文单位:AstroLightyearParsecAngstrom
  • 特殊单位:MicronNanometerGigameter

单位转换因子:

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系列:A0A1A2A3A4
  • ANSI系列:Ansi_CAnsi_DAnsi_E
  • Arch系列:Arch_AArch_BArch_CArch_DArch_E
  • 北美标准:LetterLegalTabloid

图纸尺寸定义:

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(度)

设计特点

  1. 完整的单位系统:支持从微观(埃)到天文(光年)的广泛单位
  2. 专业的格式支持:建筑、工程、科学等多种专业格式
  3. 强大的角度解析:支持测量员、弧度、百分度等特殊角度格式
  4. 国际化支持:使用Qt的翻译系统,支持多语言
  5. 错误处理完善:包含NaN检查、数值范围验证和详细的错误信息
  6. 性能优化:使用静态方法和正则表达式预编译提高效率

应用场景

  1. CAD尺寸标注:自动根据设置的单位和格式显示尺寸
  2. 数据导入导出:处理DXF文件中的单位信息
  3. 用户输入解析:智能解析用户输入的各种格式的角度和长度
  4. 图纸输出:按不同标准和格式输出图纸尺寸
  5. 单位换算工具:在英制和公制系统间转换数据

这个类体现了专业CAD软件在计量单位和格式处理方面的深度需求,是LibreCAD能够处理国际标准和专业工程需求的关键组件。

相关推荐
我是一棵无人问荆的小草2 小时前
编码演变史
开发语言·c++
偶像你挑的噻3 小时前
2.Qt-基础核心以及信号与槽
开发语言·qt
potato_may3 小时前
CC++ 内存管理 —— 程序的“五脏六腑”在哪里?
c语言·开发语言·数据结构·c++·内存·内存管理
饕餮怪程序猿3 小时前
A*算法(C++实现)
开发语言·c++·算法
ULTRA??4 小时前
C/C++函数指针
c语言·开发语言·c++
还没想好取啥名4 小时前
C++11新特性(一)——自动类型推导
开发语言·c++·stl
我是华为OD~HR~栗栗呀4 小时前
华为OD-C面经-23届学院哦
java·c++·python·华为od·华为·面试
西贝爱学习4 小时前
Visual Studio下载地址,vs2022安装程序
c++
天赐学c语言4 小时前
12.5 - 二叉树的最近公共祖先 && 构造函数和析构函数可以是虚函数吗
c++·二叉树·虚函数