Files
ViewDesignEngine/include/vde/foundation/format_utils.h
T

139 lines
4.1 KiB
C++
Raw Normal View History

#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