#pragma once #include "vde/core/point.h" #include #include #include #include #include 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(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