diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 61a32678b..9602ebdbd 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -1,5 +1,4 @@ #include "Arrange.hpp" -#include "SVG.hpp" #include "BoundingBox.hpp" diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 152d72079..b98cc5f04 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2,6 +2,7 @@ #include "../Exception.hpp" #include "../Model.hpp" #include "../Utils.hpp" +#include "../LocalesUtils.hpp" #include "../GCode.hpp" #include "../Geometry.hpp" #include "../GCode/ThumbnailData.hpp" @@ -2408,6 +2409,7 @@ namespace Slic3r { }; auto format_coordinate = [](float f, char *buf) -> char* { + assert(is_decimal_separator_point()); #if EXPORT_3MF_USE_SPIRIT_KARMA_FP // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter, // https://github.com/boostorg/spirit/pull/586 @@ -2575,6 +2577,7 @@ namespace Slic3r { bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model) { + assert(is_decimal_separator_point()); std::string out = ""; char buffer[1024]; @@ -2666,6 +2669,7 @@ namespace Slic3r { bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model) { + assert(is_decimal_separator_point()); std::string out = ""; char buffer[1024]; @@ -2700,6 +2704,7 @@ namespace Slic3r { bool _3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model) { + assert(is_decimal_separator_point()); const char *const fmt = "object_id=%d|"; std::string out; @@ -2750,6 +2755,7 @@ namespace Slic3r { bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config) { + assert(is_decimal_separator_point()); char buffer[1024]; sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str()); std::string out = buffer; @@ -2926,6 +2932,9 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c if (path == nullptr || config == nullptr || model == nullptr) return false; + // All import should use "C" locales for number formatting. + CNumericLocalesSetter locales_setter; + _3MF_Importer importer; bool res = importer.load_model_from_file(path, *model, *config, check_version); importer.log_errors(); @@ -2934,6 +2943,9 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64) { + // All export should use "C" locales for number formatting. + CNumericLocalesSetter locales_setter; + if (path == nullptr || model == nullptr) return false; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 1c9b6b27d..94318a930 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -15,6 +15,7 @@ #include "../I18N.hpp" #include "../Geometry.hpp" #include "../CustomGCode.hpp" +#include "../LocalesUtils.hpp" #include "AMF.hpp" @@ -498,6 +499,7 @@ void AMFParserContext::characters(const XML_Char *s, int len) void AMFParserContext::endElement(const char * /* name */) { + assert(is_decimal_separator_point()); switch (m_path.back()) { // Constellation transformation: @@ -1052,6 +1054,8 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model // If config is not a null pointer, updates it if the amf file/archive contains config data bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version) { + CNumericLocalesSetter locales_setter; // use "C" locales and point as a decimal separator + if (boost::iends_with(path, ".amf.xml")) // backward compatibility with older slic3r output return load_amf_file(path, config, model); @@ -1251,40 +1255,25 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, stream << " \n"; if (!object->instances.empty()) { for (ModelInstance *instance : object->instances) { - char buf[512]; - ::sprintf(buf, - " \n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %lf\n" - " %d\n" - " \n", - object_id, - instance->get_offset(X), - instance->get_offset(Y), - instance->get_offset(Z), - instance->get_rotation(X), - instance->get_rotation(Y), - instance->get_rotation(Z), - instance->get_scaling_factor(X), - instance->get_scaling_factor(Y), - instance->get_scaling_factor(Z), - instance->get_mirror(X), - instance->get_mirror(Y), - instance->get_mirror(Z), - instance->printable); + std::stringstream buf; + buf << " \n" + << " " << instance->get_offset(X) << "\n" + << " " << instance->get_offset(Y) << "\n" + << " " << instance->get_offset(Z) << "\n" + << " " << instance->get_rotation(X) << "\n" + << " " << instance->get_rotation(Y) << "\n" + << " " << instance->get_rotation(Z) << "\n" + << " " << instance->get_scaling_factor(X) << "\n" + << " " << instance->get_scaling_factor(Y) << "\n" + << " " << instance->get_scaling_factor(Z) << "\n" + << " " << instance->get_mirror(X) << "\n" + << " " << instance->get_mirror(Y) << "\n" + << " " << instance->get_mirror(Z) << "\n" + << " " << instance->printable << "\n" + << " \n"; //FIXME missing instance->scaling_factor - instances.append(buf); + instances.append(buf.str()); } } } diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp index 3e33d4aa3..b4e2c54ab 100644 --- a/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -13,6 +13,6 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, // The model could be modified during the export process if meshes are not repaired or have no shared vertices extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources); -}; // namespace Slic3r +} // namespace Slic3r #endif /* slic3r_Format_AMF_hpp_ */ diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp index 12cee350d..6cbef7b77 100644 --- a/src/libslic3r/Format/objparser.cpp +++ b/src/libslic3r/Format/objparser.cpp @@ -6,6 +6,8 @@ #include "objparser.hpp" +#include "libslic3r/LocalesUtils.hpp" + namespace ObjParser { static bool obj_parseline(const char *line, ObjData &data) @@ -15,6 +17,8 @@ static bool obj_parseline(const char *line, ObjData &data) if (*line == 0) return true; + assert(is_decimal_separator_point()); + // Ignore whitespaces at the beginning of the line. //FIXME is this a good idea? EATWS(); @@ -322,6 +326,8 @@ static bool obj_parseline(const char *line, ObjData &data) bool objparse(const char *path, ObjData &data) { + Slic3r::CNumericLocalesSetter locales_setter; + FILE *pFile = boost::nowide::fopen(path, "rt"); if (pFile == 0) return false; @@ -365,6 +371,8 @@ bool objparse(const char *path, ObjData &data) bool objparse(std::istream &stream, ObjData &data) { + Slic3r::CNumericLocalesSetter locales_setter; + try { char buf[65536 * 2]; size_t len = 0; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 61c835a4b..f4b38e2ec 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -670,7 +670,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)); std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time)); - is_last |= (std::stof(time_float_str) > 0.0f && std::stof(next_time_float_str) == 0.0f); + is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.); } if (is_last) { @@ -1373,7 +1373,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename) if (pos != cmt.npos) { pos = cmt.find(',', pos); if (pos != cmt.npos) { - out = std::stod(cmt.substr(pos + 1)); + out = string_to_double_decimal_point(cmt.substr(pos+1)); return true; } } @@ -1523,9 +1523,9 @@ template else if constexpr (std::is_same_v) out = std::stol(str, &read); else if constexpr (std::is_same_v) - out = std::stof(str, &read); + out = string_to_double_decimal_point(str, &read); else if constexpr (std::is_same_v) - out = std::stod(str, &read); + out = string_to_double_decimal_point(str, &read); return str.size() == read; } catch (...) { return false; diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index c3f084a24..48a16a8d5 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -4,6 +4,7 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" +#include "../LocalesUtils.hpp" #include "PressureEqualizer.hpp" @@ -158,7 +159,7 @@ static inline int parse_int(const char *&line) static inline float parse_float(const char *&line) { char *endptr = NULL; - float result = strtof(line, &endptr); + float result = string_to_double_decimal_point(line, &endptr); if (endptr == NULL || !is_ws_or_eol(*endptr)) throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float"); line = endptr; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 9753e4820..5ab1ae50c 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -6,6 +6,8 @@ #include #include +#include "LocalesUtils.hpp" + #include namespace Slic3r { @@ -25,6 +27,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config) const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command) { PROFILE_FUNC(); + CNumericLocalesSetter locales_setter; // for strtod // command and args const char *c = ptr; @@ -150,6 +153,7 @@ bool GCodeReader::GCodeLine::has(char axis) const bool GCodeReader::GCodeLine::has_value(char axis, float &value) const { + CNumericLocalesSetter locales_setter; // for strtod const char *c = m_raw.c_str(); // Skip the whitespaces. c = skip_whitespaces(c); diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index d98145e4b..5ee6ac58e 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -1,5 +1,6 @@ #include "LocalesUtils.hpp" +#include namespace Slic3r { @@ -39,6 +40,22 @@ bool is_decimal_separator_point() return str[1] == '.'; } + +double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/) +{ + double out; + std::istringstream stream(str); + if (! (stream >> out)) + throw std::invalid_argument("string_to_double_decimal_point conversion failed."); + if (pos) { + if (stream.eof()) + *pos = str.size(); + else + *pos = stream.tellg(); + } + return out; +} + std::string float_to_string_decimal_point(double value, int precision/* = -1*/) { assert(is_decimal_separator_point()); diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp index 74281a157..c74c8b1ac 100644 --- a/src/libslic3r/LocalesUtils.hpp +++ b/src/libslic3r/LocalesUtils.hpp @@ -40,7 +40,7 @@ bool is_decimal_separator_point(); // (We use user C locales and "C" C++ locales in most of the code.) std::string float_to_string_decimal_point(double value, int precision = -1); std::string float_to_string_decimal_point(float value, int precision = -1); - +double string_to_double_decimal_point(const std::string& str, size_t* pos = nullptr); } // namespace Slic3r diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 1fe28eb34..677577054 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1215,8 +1215,8 @@ boost::any& Choice::get_value() return m_value; } -void Choice::enable() { dynamic_cast(window)->Enable(); }; -void Choice::disable() { dynamic_cast(window)->Disable(); }; +void Choice::enable() { dynamic_cast(window)->Enable(); } +void Choice::disable() { dynamic_cast(window)->Disable(); } void Choice::msw_rescale() { diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index f3a8f6ba1..f4dbcfc43 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -2,6 +2,8 @@ #include "GUI_App.hpp" #include "I18N.hpp" +#include "libslic3r/LocalesUtils.hpp" + #include #include @@ -113,7 +115,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt str.pop_back(); percent = true; } - double val = stod(str); + double val = string_to_double_decimal_point(str); config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); break;} case coPercent: diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4cf71363d..5c34d7de0 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -11,6 +11,7 @@ #include "Camera.hpp" #include "Plater.hpp" +#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/Model.hpp" #if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA #include "libslic3r/PresetBundle.hpp" @@ -1927,7 +1928,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - double max_z = std::stod(field.substr(pos + 1)); + double max_z = string_to_double_decimal_point(field.substr(pos + 1)); // extract min_z field = field.substr(0, pos); @@ -1935,7 +1936,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co if (pos == std::string::npos) return; - const double min_z = std::stod(field.substr(pos + 1)); + const double min_z = string_to_double_decimal_point(field.substr(pos + 1)); // extract type field = field.substr(0, pos);