139 lines
4.1 KiB
C++
139 lines
4.1 KiB
C++
#pragma once
|
||
#include "vde/core/point.h"
|
||
#include <string>
|
||
#include <sstream>
|
||
#include <iomanip>
|
||
#include <cmath>
|
||
#include <cstdio>
|
||
|
||
namespace vde::foundation {
|
||
|
||
/**
|
||
* @brief 以 CAD 级别精度格式化双精度浮点数
|
||
*
|
||
* 输出科学计数法字符串,保证 12 位有效数字,满足 CAD 数据交换精度要求。
|
||
* NaN 被格式化为 "0.0",Inf/ -Inf 被裁剪为安全边界值。
|
||
*
|
||
* @param v 要格式化的双精度数
|
||
* @param precision 有效数字位数(默认 12)
|
||
* @return 格式化后的字符串,如 "1.234567890123e+02"
|
||
*
|
||
* @note 总是确保指数部分带符号(如 "e+00" 而非 "e00"),便于解析器统一处理
|
||
*
|
||
* @code{.cpp}
|
||
* auto s = fmt_double(3.14159265358979);
|
||
* // s == "3.141592653590e+00"
|
||
* @endcode
|
||
*
|
||
* @see fmt_iges_real, fmt_point
|
||
* @ingroup foundation
|
||
*/
|
||
inline std::string fmt_double(double v, int precision = 12) {
|
||
if (std::isnan(v)) return "0.0";
|
||
if (std::isinf(v)) return v > 0 ? "1e30" : "-1e30";
|
||
std::ostringstream ss;
|
||
ss << std::setprecision(precision) << std::scientific << v;
|
||
std::string s = ss.str();
|
||
// Ensure always has exponent sign (e.g., "1.0e+00" not "1.0e00")
|
||
auto epos = s.find('e');
|
||
if (epos != std::string::npos) {
|
||
if (epos + 1 < s.size() && s[epos + 1] != '-' && s[epos + 1] != '+') {
|
||
s.insert(epos + 1, "+");
|
||
}
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/**
|
||
* @brief IGES 格式实数输出
|
||
*
|
||
* 与 fmt_double() 相同,但使用 IGES 标准的 'D' 指数标记代替 'E'。
|
||
*
|
||
* @param v 要格式化的双精度数
|
||
* @return IGES 格式字符串,如 "3.141592653590D+00"
|
||
*
|
||
* @code{.cpp}
|
||
* auto s = fmt_iges_real(3.14159265358979);
|
||
* // s == "3.141592653590D+00"
|
||
* @endcode
|
||
*
|
||
* @see fmt_double
|
||
* @ingroup foundation
|
||
*/
|
||
inline std::string fmt_iges_real(double v, int /*precision*/ = 12) {
|
||
auto s = fmt_double(v, 12);
|
||
auto pos = s.find('e');
|
||
if (pos != std::string::npos) s[pos] = 'D';
|
||
return s;
|
||
}
|
||
|
||
/**
|
||
* @brief 格式化三维点为逗号分隔字符串
|
||
*
|
||
* 输出 "x,y,z" 形式,常用于 IGES/STEP/OBJ 等文本格式的顶点坐标行。
|
||
*
|
||
* @param p 三维点
|
||
* @param use_d 若为 true,使用 IGES 风格 D 指数标记
|
||
* @return 逗号分隔的坐标字符串
|
||
*
|
||
* @code{.cpp}
|
||
* Point3D p(1.0, 2.5, -3.14);
|
||
* auto s = fmt_point(p);
|
||
* // s == "1.000000000000e+00,2.500000000000e+00,-3.140000000000e+00"
|
||
* @endcode
|
||
*
|
||
* @see fmt_double, fmt_iges_real
|
||
* @ingroup foundation
|
||
*/
|
||
inline std::string fmt_point(const core::Point3D& p, bool use_d = false) {
|
||
using FmtFn = std::string (*)(double, int);
|
||
FmtFn fn = use_d ? static_cast<FmtFn>(fmt_iges_real) : fmt_double;
|
||
return fn(p.x(), 12) + "," + fn(p.y(), 12) + "," + fn(p.z(), 12);
|
||
}
|
||
|
||
/**
|
||
* @brief 将字符串填充到恰好 72 个字符
|
||
*
|
||
* IGES 格式中每条数据行固定为 72 字符宽。不足用空格补齐,超出则截断。
|
||
*
|
||
* @param s 输入字符串
|
||
* @return 恰好 72 字符的字符串(不足右侧补空格,超出截断)
|
||
*
|
||
* @see format_iges_line
|
||
* @ingroup foundation
|
||
*/
|
||
inline std::string pad72(const std::string& s) {
|
||
std::string result = s;
|
||
if (result.size() > 72) result.resize(72);
|
||
result.resize(72, ' ');
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* @brief 格式化 IGES 段落行
|
||
*
|
||
* 生成一条完整的 IGES 80 列记录行:72 列数据 + 8 列序号(section_char + 7 位数字)。
|
||
*
|
||
* @param data 数据内容(最多 72 字符,超出截断)
|
||
* @param section_char 段落标识符(如 'P' 参数段, 'D' 目录段)
|
||
* @param seq 行序号(1-based)
|
||
* @return 完整 IGES 行(含换行符),共 81 字符
|
||
*
|
||
* @code{.cpp}
|
||
* auto line = format_iges_line("110,1.0,2.0,3.0;", 'P', 1);
|
||
* // 输出: "110,1.0,2.0,3.0; P0000001\n"
|
||
* @endcode
|
||
*
|
||
* @see pad72
|
||
* @ingroup foundation
|
||
*/
|
||
inline std::string format_iges_line(const std::string& data, char section_char, int seq) {
|
||
std::string line = pad72(data);
|
||
char seq_buf[9];
|
||
std::snprintf(seq_buf, sizeof(seq_buf), "%c%07d", section_char, seq);
|
||
line += std::string(seq_buf);
|
||
return line + "\n";
|
||
}
|
||
|
||
} // namespace vde::foundation
|