#pragma once #include "vde/core/point.h" #include #include #include #include #include namespace vde::foundation { /// Format double with adequate precision for CAD (12 significant digits) 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; } /// Format for IGES: uses D exponent 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; } /// Format a Point3D as comma-separated reals 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); } /// Pad a string to exactly 72 characters with spaces inline std::string pad72(const std::string& s) { std::string result = s; if (result.size() > 72) result.resize(72); result.resize(72, ' '); return result; } /// Format an IGES section line: data + section marker (80 columns total) 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