From 116e2b2112b682efd775615f88e76f86b26562ad Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 19 May 2021 16:09:45 +0200 Subject: [PATCH 01/28] Fixing the slice_mesh() after recent refactoring. --- src/libslic3r/TriangleMeshSlicer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index e27766fb86..ea9eb5ff29 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -1075,7 +1075,7 @@ std::vector slice_mesh( auto t = params.trafo; t.prescale(Vec3d(s, s, 1.)); auto tf = t.cast(); - slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, facets_edges, zs, throw_on_cancel); + lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, facets_edges, zs, throw_on_cancel); } } else { // Copy and scale vertices in XY, don't scale in Z. From ff6623b0f47cc8d47f7335c1de9f9eeba96afd45 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 19 May 2021 16:51:25 +0200 Subject: [PATCH 02/28] Fixing --- src/libslic3r/TriangleMesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5e18e43ef6..c81f9066ed 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -911,8 +911,8 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c const Vec3f &p2 = pts[iedge]; if ((p1.z() < z && p2.z() > z) || (p2.z() < z && p1.z() > z)) { // Edge crosses the z plane. Calculate intersection point with the plane. - float t = z / (p2.z() - p1.z()); - all_pts.emplace_back(scaled(p1.x() + (p2.x() - p1.x()) * t), scaled(p2.x() + (p2.y() - p2.y()) * t)); + float t = (z - p1.z()) / (p2.z() - p1.z()); + all_pts.emplace_back(scaled(p1.x() + (p2.x() - p1.x()) * t), scaled(p1.y() + (p2.y() - p1.y()) * t)); } if (p2.z() > z) all_pts.emplace_back(scaled(p2.x()), scaled(p2.y())); From 8377b6ef4f9b27b9fc26d9f6ee7bffbfbccd4062 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 May 2021 09:09:19 +0200 Subject: [PATCH 03/28] Fixed typo in its_collect_mesh_projection_points_above() and method TriangleMesh::slice() set as const --- src/libslic3r/TriangleMesh.cpp | 4 ++-- src/libslic3r/TriangleMesh.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index c81f9066ed..8648edc528 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -611,7 +611,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const return output_mesh; } -std::vector TriangleMesh::slice(const std::vector &z) +std::vector TriangleMesh::slice(const std::vector &z) const { // convert doubles to floats std::vector z_f(z.begin(), z.end()); @@ -905,7 +905,7 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c all_pts.reserve(all_pts.size() + its.indices.size() * 3); for (const stl_triangle_vertex_indices &tri : its.indices) { const Vec3f pts[3] = { transform_fn(its.vertices[tri(0)]), transform_fn(its.vertices[tri(1)]), transform_fn(its.vertices[tri(2)]) }; - int iprev = 3; + int iprev = 2; for (int iedge = 0; iedge < 3; ++ iedge) { const Vec3f &p1 = pts[iprev]; const Vec3f &p2 = pts[iedge]; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 29a42eab3a..49b11465e9 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -65,7 +65,7 @@ public: // Returns the convex hull of this TriangleMesh TriangleMesh convex_hull_3d() const; // Slice this mesh at the provided Z levels and return the vector - std::vector slice(const std::vector& z); + std::vector slice(const std::vector& z) const; void reset_repair_stats(); bool needed_repair() const; void require_shared_vertices(); From a218e0ef1860df6865f0bbcead02a4f1993147ab Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 May 2021 09:32:17 +0200 Subject: [PATCH 04/28] Tech ENABLE_ALLOW_NEGATIVE_Z -> New implementation for method ModelObject::convex_hull_2d() --- src/libslic3r/Model.cpp | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f916fbdcd6..21b13cbc2b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,3 +1,4 @@ +#include "libslic3r.h" #include "Exception.hpp" #include "Model.hpp" #include "ModelArrange.hpp" @@ -889,35 +890,22 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ // Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane. // This method is cheap in that it does not make any unnecessary copy of the volume meshes. // This method is used by the auto arrange function. +#if ENABLE_ALLOW_NEGATIVE_Z +Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const +{ + Points pts; + for (const ModelVolume* v : volumes) { + if (v->is_model_part()) + append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast(), 0.0f).points); + } + return Geometry::convex_hull(std::move(pts)); +} +#else Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const { Points pts; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { -#if ENABLE_ALLOW_NEGATIVE_Z - const Transform3d trafo = trafo_instance * v->get_matrix(); - const TriangleMesh& hull_3d = v->get_convex_hull(); - const indexed_triangle_set& its = hull_3d.its; - if (its.vertices.empty()) { - // Using the STL faces. - const stl_file& stl = hull_3d.stl; - for (const stl_facet& facet : stl.facet_start) { - for (size_t j = 0; j < 3; ++j) { - const Vec3d p = trafo * facet.vertex[j].cast(); - if (p.z() >= 0.0) - pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); - } - } - } - else { - // Using the shared vertices should be a bit quicker than using the STL faces. - for (size_t i = 0; i < its.vertices.size(); ++i) { - const Vec3d p = trafo * its.vertices[i].cast(); - if (p.z() >= 0.0) - pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); - } - } -#else Transform3d trafo = trafo_instance * v->get_matrix(); const indexed_triangle_set &its = v->mesh().its; if (its.vertices.empty()) { @@ -935,10 +923,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } } -#endif // ENABLE_ALLOW_NEGATIVE_Z } return Geometry::convex_hull(std::move(pts)); } +#endif // ENABLE_ALLOW_NEGATIVE_Z void ModelObject::center_around_origin(bool include_modifiers) { From eb431db7ed9d76008078add31552b74d5af19c27 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 May 2021 11:04:47 +0200 Subject: [PATCH 05/28] Test --- src/slic3r/GUI/GCodeViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ce4c18a53c..209679c0fc 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -591,7 +591,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& m_last_result_id = gcode_result.id; // release gpu memory, if used - reset(); + reset(); #if ENABLE_GCODE_WINDOW m_sequential_view.gcode_window.set_filename(gcode_result.filename); From f0ef5e409dd15d12efdbe447bc68505ddf1dc802 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 May 2021 12:53:47 +0200 Subject: [PATCH 06/28] Added unit test for calculation of 2D convex hull of sinking object --- tests/libslic3r/test_3mf.cpp | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index fb41ef93b5..d6d3686d98 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -85,3 +85,43 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { } } } + +SCENARIO("2D convex hull of sinking object", "[3mf]") { + GIVEN("model") { + // load a model + Model model; + std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/Prusa.stl"; + load_stl(src_file.c_str(), &model); + model.add_default_instances(); + + WHEN("model is rotated, scaled and set as sinking") { + ModelObject* object = model.objects[0]; + object->center_around_origin(false); + + // set instance's attitude so that it is rotated, scaled and sinking + ModelInstance* instance = object->instances[0]; + instance->set_rotation(Y, -M_PI / 4.0); + instance->set_offset(Vec3d::Zero()); + instance->set_scaling_factor({ 2.0, 2.0, 2.0 }); + + // calculate 2D convex hull + Polygon hull_2d = object->convex_hull_2d(instance->get_transformation().get_matrix()); + + // verify result + Points result = { + { -4242641, -16299551 }, + { -4241, -19502998 }, + { 66824768, -19502998 }, + { 66824768, 19502998 }, + { -4244, 19502998 }, + { -4242640, -8537523 } + }; + bool res = hull_2d.points == result; + + THEN("2D convex hull should match with reference") { + REQUIRE(res); + } + } + } +} + From 635bf8d9f14e3556d2f43ee09540360409c01140 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 20 May 2021 13:09:25 +0200 Subject: [PATCH 07/28] Notifications: Calculating space left for hypertext with escaped text --- src/slic3r/GUI/NotificationManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 1b29b7f765..921f9f2b15 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -6,6 +6,7 @@ #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "../Utils/PrintHost.hpp" +#include "libslic3r/Config.hpp" #include #include @@ -304,8 +305,8 @@ void NotificationManager::PopNotification::count_lines() } // hypertext calculation if (!m_hypertext.empty()) { - int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0; - if (ImGui::CalcTextSize((text.substr(prev_end, last_end - prev_end) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) { + int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0; // m_endlines.size() - 2 because we are fitting hypertext instead of last endline + if (ImGui::CalcTextSize((escape_string_cstyle(text.substr(prev_end, last_end - prev_end)) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) { m_endlines.push_back(last_end); m_lines_count++; } @@ -366,7 +367,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons } //hyperlink text if (!m_hypertext.empty()) { - render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext); + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext); } From ef9004cee462b96a0b7e4910f9983fe73586b530 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 20 May 2021 13:58:27 +0200 Subject: [PATCH 08/28] Fix of its_collect_mesh_projection_points_above() Fix of perl integration tests after introduction of not using the mesh below the print bed. --- lib/Slic3r/Test.pm | 4 ++-- src/libslic3r/TriangleMesh.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 570bca41bc..8d2a79f2a4 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -192,12 +192,12 @@ sub init_print { if (defined $params{duplicate} && $params{duplicate} > 1) { $model->duplicate($params{duplicate} // 1, $config->min_object_distance); } - $model->arrange_objects($config->min_object_distance); - $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); foreach my $model_object (@{$model->objects}) { $model_object->ensure_on_bed; $print->auto_assign_extruders($model_object); } + $model->arrange_objects($config->min_object_distance); + $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); $print->apply($model, $config); $print->validate; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 8648edc528..fba1429ea2 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -914,7 +914,7 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c float t = (z - p1.z()) / (p2.z() - p1.z()); all_pts.emplace_back(scaled(p1.x() + (p2.x() - p1.x()) * t), scaled(p1.y() + (p2.y() - p1.y()) * t)); } - if (p2.z() > z) + if (p2.z() >= z) all_pts.emplace_back(scaled(p2.x()), scaled(p2.y())); iprev = iedge; } From 2bd3e018b129358b5b952487ae5e78038e2c57cc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 May 2021 14:42:40 +0200 Subject: [PATCH 09/28] Follow-up of d54548367a946f4c1c388a91fca61f1589533fa2 -> Reintroduce GLCanvas3D::m_old_size to avoid unneeded calls to render() --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 8 ++++++++ src/slic3r/GUI/Plater.cpp | 10 +++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ea3bec1311..3829ab0d15 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4527,6 +4527,14 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) if (m_canvas == nullptr && m_context == nullptr) return; +#if ENABLE_SCROLLABLE_LEGEND + const std::array new_size = { w, h }; + if (m_old_size == new_size) + return; + + m_old_size = new_size; +#endif // ENABLE_SCROLLABLE_LEGEND + auto *imgui = wxGetApp().imgui(); imgui->set_display_size(static_cast(w), static_cast(h)); const float font_size = 1.5f * wxGetApp().em_unit(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5c010d959..f7493f1572 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -425,6 +425,10 @@ private: Model* m_model; BackgroundSlicingProcess *m_process; +#if ENABLE_SCROLLABLE_LEGEND + std::array m_old_size{ 0, 0 }; +#endif // ENABLE_SCROLLABLE_LEGEND + // Screen is only refreshed from the OnIdle handler if it is dirty. bool m_dirty; bool m_initialized; @@ -733,6 +737,10 @@ public: #endif } +#if ENABLE_SCROLLABLE_LEGEND + void reset_old_size() { m_old_size = { 0, 0 }; } +#endif // ENABLE_SCROLLABLE_LEGEND + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e306641a96..3aff82fb44 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3437,8 +3437,12 @@ void Plater::priv::set_current_panel(wxPanel* panel) // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); +#if ENABLE_SCROLLABLE_LEGEND + // reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size + view3D->get_canvas3d()->reset_old_size(); +#endif // ENABLE_SCROLLABLE_LEGEND view_toolbar.select_item("3D"); - if(notification_manager != nullptr) + if (notification_manager != nullptr) notification_manager->set_in_preview(false); } else if (current_panel == preview) { @@ -3457,6 +3461,10 @@ void Plater::priv::set_current_panel(wxPanel* panel) preview->reload_print(true); preview->set_as_dirty(); +#if ENABLE_SCROLLABLE_LEGEND + // reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size + preview->get_canvas3d()->reset_old_size(); +#endif // ENABLE_SCROLLABLE_LEGEND view_toolbar.select_item("Preview"); if (notification_manager != nullptr) notification_manager->set_in_preview(true); From 6bb2982b98644916d721cadd627ebc9fbc2e38ac Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 May 2021 15:34:59 +0200 Subject: [PATCH 10/28] Skip picking pass when plater is empty --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3829ab0d15..734bc24aa6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1356,7 +1356,7 @@ void GLCanvas3D::render() if (m_rectangle_selection.is_dragging()) // picking pass using rectangle selection _rectangular_selection_picking_pass(); - else + else if (!m_volumes.empty()) // regular picking pass _picking_pass(); } From f50046b31bc2b8c7ef36e21778cf5e889e945d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 20 May 2021 19:44:12 +0200 Subject: [PATCH 11/28] Modified streaming interface in miniz library to use ZIP64 extension only for files exciding the limits. The modification is based on a preallocation space in the Extra field using a phony custom block in the Local file header. This space is eventually overwritten by the ZIP64 extension if the file exceeds 4GiB. Also were fixed differences from PKZip specification in the streaming interface. --- src/miniz/miniz.c | 119 ++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 51 deletions(-) diff --git a/src/miniz/miniz.c b/src/miniz/miniz.c index 08126f441a..cff9aa5d38 100644 --- a/src/miniz/miniz.c +++ b/src/miniz/miniz.c @@ -5953,17 +5953,16 @@ static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { - (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, (pZip->m_pState->m_zip64) ? 0x002D : (method ? 0x0014 : 0x0000)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || comp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, (pZip->m_pState->m_zip64 || uncomp_size > MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; @@ -5975,10 +5974,12 @@ static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_ mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { - (void)pZip; + mz_zip_internal_state *pState = pZip->m_pState; + mz_bool is_zip64_needed = uncomp_size >= MZ_UINT32_MAX || comp_size >= MZ_UINT32_MAX; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, is_zip64_needed ? 0x002D : (method ? 0x0014 : 0x0000)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); @@ -6675,6 +6676,15 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA return MZ_TRUE; } +static mz_uint32 mz_zip_writer_preallocate_extra_data(mz_uint8 *pBuf, mz_uint32 extra_data_size) +{ + mz_uint8 *pDst = pBuf; + MZ_WRITE_LE16(pDst + 0, 0x9999); + MZ_WRITE_LE16(pDst + 2, extra_data_size); + memset(pDst + 4, 0, extra_data_size); + return 2 * sizeof(mz_uint16) + extra_data_size; +} + mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged_context* pContext, const char* pArchive_name, mz_uint64 max_size, const MZ_TIME_T* pFile_time, const void* pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char* user_extra_data, mz_uint user_extra_data_len, const char* user_extra_data_central, mz_uint user_extra_data_central_len) { @@ -6708,13 +6718,6 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged pState = pZip->m_pState; - if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) - { - /* Source file is too large for non-zip64 */ - /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ - pState->m_zip64 = MZ_TRUE; - } - /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); @@ -6778,55 +6781,45 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged MZ_ASSERT((pContext->cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + if (!pState->m_zip64 && (pContext->local_dir_header_ofs >= MZ_UINT32_MAX)) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + MZ_CLEAR_OBJ(local_dir_header); if (pState->m_zip64) { - if (max_size >= MZ_UINT32_MAX || pContext->local_dir_header_ofs >= MZ_UINT32_MAX) - { - pContext->pExtra_data = pContext->extra_data; - pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (max_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL, - (max_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); - } + pContext->pExtra_data = pContext->extra_data; + pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, + (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); + } + else if (!pState->m_zip64 && max_size > MZ_UINT32_MAX) + { + pContext->pExtra_data = pContext->extra_data; + pContext->extra_size = mz_zip_writer_preallocate_extra_data(pContext->extra_data, 2 * sizeof(mz_uint64)); + } - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)(pContext->extra_size + user_extra_data_len), 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date)) - return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)(pContext->extra_size + user_extra_data_len), 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); - if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - pContext->cur_archive_file_ofs += sizeof(local_dir_header); + pContext->cur_archive_file_ofs += sizeof(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size) - { - return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - } + if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } - pContext->cur_archive_file_ofs += pContext->archive_name_size; + pContext->cur_archive_file_ofs += pContext->archive_name_size; + if (pState->m_zip64 || max_size >= MZ_UINT32_MAX) { if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pContext->extra_data, pContext->extra_size) != pContext->extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pContext->cur_archive_file_ofs += pContext->extra_size; } - else - { - if ((pContext->comp_size > MZ_UINT32_MAX) || (pContext->cur_archive_file_ofs > MZ_UINT32_MAX)) - return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)pContext->archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, pContext->method, pContext->gen_flags, pContext->dos_time, pContext->dos_date)) - return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - - pContext->cur_archive_file_ofs += sizeof(local_dir_header); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pContext->cur_archive_file_ofs, pArchive_name, pContext->archive_name_size) != pContext->archive_name_size) - { - return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - } - - pContext->cur_archive_file_ofs += pContext->archive_name_size; - } if (user_extra_data_len > 0) { @@ -6898,17 +6891,40 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext) pContext->pZip->m_pFree(pContext->pZip->m_pAlloc_opaque, pContext->pCompressor); pContext->pCompressor = NULL; + // Rewrite preallocated phony custom block in local dir header by ZIP64 extension. Also, other values are adjusted in the header. + if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX) { + mz_uint64 local_dir_header_ofs = pContext->local_dir_header_ofs; + pContext->pExtra_data = pContext->extra_data; + pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); + + mz_uint8 min_version[2] = {0x2D, 0x00}; + mz_uint8 comp_uncomp_size[4] = {0xFF, 0xFF, 0xFF, 0xFF}; + if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_VERSION_NEEDED_OFS, min_version, sizeof(min_version)) != sizeof(min_version)) + return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED); + if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size)) + return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED); + if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, comp_uncomp_size, sizeof(comp_uncomp_size)) != sizeof(comp_uncomp_size)) + return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED); + if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + pContext->archive_name_size, pContext->extra_data, pContext->extra_size) != pContext->extra_size) + return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + pContext->uncomp_size = pContext->file_ofs; pContext->comp_size = pContext->add_state.m_comp_size; pContext->cur_archive_file_ofs = pContext->add_state.m_cur_archive_file_ofs; + // Clean pContext->pExtra_data, pContext->extra_data and pContext->extra_size after calling mz_zip_writer_add_staged_open + pContext->pExtra_data = NULL; + pContext->extra_size = 0; + memset(pContext->extra_data, 0, sizeof(mz_uint8) * MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE); + { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, pContext->uncomp_crc32); - if (pContext->pExtra_data == NULL) + if (pContext->uncomp_size < MZ_UINT32_MAX && pContext->comp_size < MZ_UINT32_MAX) { if (pContext->comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pContext->pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); @@ -6929,10 +6945,11 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext) pContext->cur_archive_file_ofs += local_dir_footer_size; } - if (pContext->pExtra_data != NULL) + if (pContext->file_ofs >= MZ_UINT32_MAX || pContext->add_state.m_comp_size >= MZ_UINT32_MAX) { + pContext->pExtra_data = pContext->extra_data; pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, (pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->uncomp_size : NULL, - (pContext->uncomp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); + (pContext->comp_size >= MZ_UINT32_MAX) ? &pContext->comp_size : NULL, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pContext->pZip, pContext->pArchive_name, (mz_uint16)pContext->archive_name_size, pContext->pExtra_data, (mz_uint16)pContext->extra_size, pContext->pComment, pContext->comment_size, From ed9c93f85a8eafd27779cf49ab2406b8f791cd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 21 May 2021 07:42:37 +0200 Subject: [PATCH 12/28] Added missing includes (GCC 9.3) --- src/libslic3r/TriangleMeshSlicer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index ea9eb5ff29..a3123ea760 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -23,6 +23,8 @@ #endif #include +#include +#include #if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING) #include "SVG.hpp" From 90be278d981d60faff8cb00a165687656fdcbfeb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 May 2021 10:14:13 +0200 Subject: [PATCH 13/28] Attempt to fix unit test for 2D convex hull of sinking object on RasperryPi --- tests/libslic3r/test_3mf.cpp | 44 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index d6d3686d98..c7c30daf6b 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -30,23 +30,23 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { load_stl(src_file.c_str(), &src_model); src_model.add_default_instances(); - ModelObject* src_object = src_model.objects[0]; + ModelObject* src_object = src_model.objects.front(); // apply generic transformation to the 1st volume Geometry::Transformation src_volume_transform; - src_volume_transform.set_offset(Vec3d(10.0, 20.0, 0.0)); - src_volume_transform.set_rotation(Vec3d(Geometry::deg2rad(25.0), Geometry::deg2rad(35.0), Geometry::deg2rad(45.0))); - src_volume_transform.set_scaling_factor(Vec3d(1.1, 1.2, 1.3)); - src_volume_transform.set_mirror(Vec3d(-1.0, 1.0, -1.0)); - src_object->volumes[0]->set_transformation(src_volume_transform); + src_volume_transform.set_offset({ 10.0, 20.0, 0.0 }); + src_volume_transform.set_rotation({ Geometry::deg2rad(25.0), Geometry::deg2rad(35.0), Geometry::deg2rad(45.0) }); + src_volume_transform.set_scaling_factor({ 1.1, 1.2, 1.3 }); + src_volume_transform.set_mirror({ -1.0, 1.0, -1.0 }); + src_object->volumes.front()->set_transformation(src_volume_transform); // apply generic transformation to the 1st instance Geometry::Transformation src_instance_transform; - src_instance_transform.set_offset(Vec3d(5.0, 10.0, 0.0)); - src_instance_transform.set_rotation(Vec3d(Geometry::deg2rad(12.0), Geometry::deg2rad(13.0), Geometry::deg2rad(14.0))); - src_instance_transform.set_scaling_factor(Vec3d(0.9, 0.8, 0.7)); - src_instance_transform.set_mirror(Vec3d(1.0, -1.0, -1.0)); - src_object->instances[0]->set_transformation(src_instance_transform); + src_instance_transform.set_offset({ 5.0, 10.0, 0.0 }); + src_instance_transform.set_rotation({ Geometry::deg2rad(12.0), Geometry::deg2rad(13.0), Geometry::deg2rad(14.0) }); + src_instance_transform.set_scaling_factor({ 0.9, 0.8, 0.7 }); + src_instance_transform.set_mirror({ 1.0, -1.0, -1.0 }); + src_object->instances.front()->set_transformation(src_instance_transform); WHEN("model is saved+loaded to/from 3mf file") { // save the model to 3mf file @@ -67,16 +67,9 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { dst_mesh.repair(); bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size(); - if (res) - { - for (size_t i = 0; i < dst_mesh.its.vertices.size(); ++i) - { + if (res) { + for (size_t i = 0; i < dst_mesh.its.vertices.size(); ++i) { res &= dst_mesh.its.vertices[i].isApprox(src_mesh.its.vertices[i]); - if (!res) - { - Vec3f diff = dst_mesh.its.vertices[i] - src_mesh.its.vertices[i]; - std::cout << i << ": diff " << to_string((Vec3d)diff.cast()) << "\n"; - } } } THEN("world vertices coordinates after load match") { @@ -95,11 +88,11 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { model.add_default_instances(); WHEN("model is rotated, scaled and set as sinking") { - ModelObject* object = model.objects[0]; + ModelObject* object = model.objects.front(); object->center_around_origin(false); // set instance's attitude so that it is rotated, scaled and sinking - ModelInstance* instance = object->instances[0]; + ModelInstance* instance = object->instances.front(); instance->set_rotation(Y, -M_PI / 4.0); instance->set_offset(Vec3d::Zero()); instance->set_scaling_factor({ 2.0, 2.0, 2.0 }); @@ -116,7 +109,12 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -4244, 19502998 }, { -4242640, -8537523 } }; - bool res = hull_2d.points == result; + bool res = hull_2d.points.size() == result.size(); + if (res) { + for (size_t i = 0; i < hull_2d.points.size(); ++i) { + res &= hull_2d.points[i].isApprox(result[i]); + } + } THEN("2D convex hull should match with reference") { REQUIRE(res); From 49f29e835334d0033771d25ff341c06b1b564b9b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 May 2021 11:56:10 +0200 Subject: [PATCH 14/28] Added debug output to help fixing unit test for 2D convex hull of sinking object on RasperryPi --- tests/libslic3r/test_3mf.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index c7c30daf6b..e865b1d78c 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -109,11 +109,13 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -4244, 19502998 }, { -4242640, -8537523 } }; - bool res = hull_2d.points.size() == result.size(); - if (res) { - for (size_t i = 0; i < hull_2d.points.size(); ++i) { - res &= hull_2d.points[i].isApprox(result[i]); - } + + bool res = hull_2d.points == result; + + std::cout << "hull_2d vertices count: " << hull_2d.points.size() << "\n"; + std::cout << "hull_2d vertices:\n"; + for (size_t i = 0; i < hull_2d.points.size(); ++i) { + std::cout << hull_2d.points[i].x() << ", " << hull_2d.points[i].y() << "\n"; } THEN("2D convex hull should match with reference") { From dfa85c2c37297f1d75f4a756a1c5f4ee3a79de67 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 May 2021 15:37:25 +0200 Subject: [PATCH 15/28] Another attempt to fix unit test for 2D convex hull of sinking object on RasperryPi --- tests/libslic3r/test_3mf.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index e865b1d78c..d988deffba 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -110,14 +110,18 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -4242640, -8537523 } }; - bool res = hull_2d.points == result; - - std::cout << "hull_2d vertices count: " << hull_2d.points.size() << "\n"; - std::cout << "hull_2d vertices:\n"; - for (size_t i = 0; i < hull_2d.points.size(); ++i) { - std::cout << hull_2d.points[i].x() << ", " << hull_2d.points[i].y() << "\n"; + bool res = hull_2d.points.size() == result.size(); + if (res) { + for (size_t i = 0; i < result.size(); ++i) { + Vec3d hull_p(unscale(hull_2d.points[i].x()), unscale(hull_2d.points[i].y()), 0.0); + Vec3d res_p(unscale(result[i].x()), unscale(result[i].y()), 0.0); + res &= res_p.isApprox(hull_p); + } } + // this does not work on RaspberryPi for float precision problem: the x of 1st vertex ending up being -4242640 instead of -4242641 +// bool res = hull_2d.points == result; + THEN("2D convex hull should match with reference") { REQUIRE(res); } From ca9fad002cf928ac977138de7d9aa96af388f492 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 May 2021 08:54:08 +0200 Subject: [PATCH 16/28] 3rd attempt to fix unit test for 2D convex hull of sinking object on RasperryPi --- tests/libslic3r/test_3mf.cpp | 56 +++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index d988deffba..666db58dce 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -93,7 +93,10 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { // set instance's attitude so that it is rotated, scaled and sinking ModelInstance* instance = object->instances.front(); - instance->set_rotation(Y, -M_PI / 4.0); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + instance->set_rotation(X, -M_PI / 4.0); +// instance->set_rotation(Y, -M_PI / 4.0); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ instance->set_offset(Vec3d::Zero()); instance->set_scaling_factor({ 2.0, 2.0, 2.0 }); @@ -101,26 +104,47 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { Polygon hull_2d = object->convex_hull_2d(instance->get_transformation().get_matrix()); // verify result +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Points result = { - { -4242641, -16299551 }, - { -4241, -19502998 }, - { 66824768, -19502998 }, - { 66824768, 19502998 }, - { -4244, 19502998 }, - { -4242640, -8537523 } + { -91501496, -15914144 }, + { 91501496, -15914144 }, + { 91501496, 4243 }, + { 78229680, 4246883 }, + { 56898100, 4246883 }, + { -85501496, 4242641 }, + { -91501496, 4243 } }; - bool res = hull_2d.points.size() == result.size(); - if (res) { - for (size_t i = 0; i < result.size(); ++i) { - Vec3d hull_p(unscale(hull_2d.points[i].x()), unscale(hull_2d.points[i].y()), 0.0); - Vec3d res_p(unscale(result[i].x()), unscale(result[i].y()), 0.0); - res &= res_p.isApprox(hull_p); - } +// Points result = { +// { -4242641, -16299551 }, +// { -4241, -19502998 }, +// { 66824768, -19502998 }, +// { 66824768, 19502998 }, +// { -4244, 19502998 }, +// { -4242640, -8537523 } +// }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + for (const Point& p : hull_2d.points) { + std::cout << p.x() << ", " << p.y() << "\n"; } - // this does not work on RaspberryPi for float precision problem: the x of 1st vertex ending up being -4242640 instead of -4242641 -// bool res = hull_2d.points == result; + bool res = hull_2d.points == result; + +// bool res = hull_2d.points.size() == result.size(); +// if (res) { +// for (size_t i = 0; i < result.size(); ++i) { +// Vec3d hull_p(unscale(hull_2d.points[i].x()), unscale(hull_2d.points[i].y()), 0.0); +// Vec3d res_p(unscale(result[i].x()), unscale(result[i].y()), 0.0); +// res &= res_p.isApprox(hull_p); +// } +// } +// +// // this does not work on RaspberryPi for float precision problem: the x of 1st vertex ending up being -4242640 instead of -4242641 +//// bool res = hull_2d.points == result; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ THEN("2D convex hull should match with reference") { REQUIRE(res); From 9da87d8e0f0730fac93f6f3bd956ace8b1f3c7b5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 May 2021 10:03:48 +0200 Subject: [PATCH 17/28] Code cleanup --- tests/libslic3r/test_3mf.cpp | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 666db58dce..d0f459e4d4 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -93,10 +93,7 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { // set instance's attitude so that it is rotated, scaled and sinking ModelInstance* instance = object->instances.front(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ instance->set_rotation(X, -M_PI / 4.0); -// instance->set_rotation(Y, -M_PI / 4.0); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ instance->set_offset(Vec3d::Zero()); instance->set_scaling_factor({ 2.0, 2.0, 2.0 }); @@ -104,7 +101,6 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { Polygon hull_2d = object->convex_hull_2d(instance->get_transformation().get_matrix()); // verify result -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Points result = { { -91501496, -15914144 }, { 91501496, -15914144 }, @@ -115,37 +111,8 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -91501496, 4243 } }; -// Points result = { -// { -4242641, -16299551 }, -// { -4241, -19502998 }, -// { 66824768, -19502998 }, -// { 66824768, 19502998 }, -// { -4244, 19502998 }, -// { -4242640, -8537523 } -// }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - - -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - for (const Point& p : hull_2d.points) { - std::cout << p.x() << ", " << p.y() << "\n"; - } - bool res = hull_2d.points == result; -// bool res = hull_2d.points.size() == result.size(); -// if (res) { -// for (size_t i = 0; i < result.size(); ++i) { -// Vec3d hull_p(unscale(hull_2d.points[i].x()), unscale(hull_2d.points[i].y()), 0.0); -// Vec3d res_p(unscale(result[i].x()), unscale(result[i].y()), 0.0); -// res &= res_p.isApprox(hull_p); -// } -// } -// -// // this does not work on RaspberryPi for float precision problem: the x of 1st vertex ending up being -4242640 instead of -4242641 -//// bool res = hull_2d.points == result; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - THEN("2D convex hull should match with reference") { REQUIRE(res); } From 5a1441f9b7b9bb6d35b97ad9cbc25a94c2700d7b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 3 May 2021 16:00:46 +0200 Subject: [PATCH 18/28] Created a RAII class to switch LC_NUMERIC to "C" and back --- src/libslic3r/CMakeLists.txt | 2 ++ src/libslic3r/LocalesUtils.cpp | 59 ++++++++++++++++++++++++++++++++++ src/libslic3r/LocalesUtils.hpp | 47 +++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/libslic3r/LocalesUtils.cpp create mode 100644 src/libslic3r/LocalesUtils.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d98d0e10fd..b7b90a8f59 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -126,6 +126,8 @@ add_library(libslic3r STATIC Line.hpp LibraryCheck.cpp LibraryCheck.hpp + LocalesUtils.cpp + LocalesUtils.hpp Model.cpp Model.hpp ModelArrange.hpp diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp new file mode 100644 index 0000000000..d98145e4b6 --- /dev/null +++ b/src/libslic3r/LocalesUtils.cpp @@ -0,0 +1,59 @@ +#include "LocalesUtils.hpp" + + +namespace Slic3r { + + +CNumericLocalesSetter::CNumericLocalesSetter() +{ +#ifdef _WIN32 + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr); + std::setlocale(LC_NUMERIC, "C"); +#else + m_original_locale = uselocale((locale_t)0); + m_new_locale = duplocale(m_original_locale); + m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale); + uselocale(m_new_locale); +#endif +} + + + +CNumericLocalesSetter::~CNumericLocalesSetter() +{ +#ifdef _WIN32 + std::setlocale(LC_NUMERIC, m_orig_numeric_locale) +#else + uselocale(m_original_locale); + freelocale(m_new_locale); +#endif +} + + + +bool is_decimal_separator_point() +{ + char str[5] = ""; + sprintf(str, "%.1f", 0.5f); + return str[1] == '.'; +} + +std::string float_to_string_decimal_point(double value, int precision/* = -1*/) +{ + assert(is_decimal_separator_point()); + std::stringstream buf; + if (precision >= 0) + buf << std::fixed << std::setprecision(precision); + buf << value; + return buf.str(); +} + +std::string float_to_string_decimal_point(float value, int precision/* = -1*/) +{ + return float_to_string_decimal_point(double(value), precision); +} + + +} // namespace Slic3r + diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp new file mode 100644 index 0000000000..74281a1575 --- /dev/null +++ b/src/libslic3r/LocalesUtils.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_LocalesUtils_hpp_ +#define slic3r_LocalesUtils_hpp_ + +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +namespace Slic3r { + +// RAII wrapper that sets LC_NUMERIC to "C" on construction +// and restores the old value on destruction. +class CNumericLocalesSetter { +public: + CNumericLocalesSetter(); + ~CNumericLocalesSetter(); + +private: +#ifdef _WIN32 + std::string m_orig_numeric_locale; +#else + locale_t m_original_locale; + locale_t m_new_locale; +#endif + +}; + +// A function to check that current C locale uses decimal point as a separator. +// Intended mostly for asserts. +bool is_decimal_separator_point(); + + +// A substitute for std::to_string that works according to +// C++ locales, not C locale. Meant to be used when we need +// to be sure that decimal point is used as a separator. +// (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); + + +} // namespace Slic3r + +#endif // slic3r_LocalesUtils_hpp_ From 9ee2fc8275eb9074a1abbc1439264f05dcee04be Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 May 2021 14:06:31 +0200 Subject: [PATCH 19/28] Fixed first batch of locale-dependent calls --- src/libslic3r/GCode.cpp | 7 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 5 +- src/libslic3r/GCode/WipeTower.cpp | 78 +++++++++++--------------- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 18 +++--- src/slic3r/GUI/GUI_App.cpp | 2 +- 6 files changed, 52 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d47d185a09..208363f351 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -12,6 +12,7 @@ #include "Utils.hpp" #include "ClipperUtils.hpp" #include "libslic3r.h" +#include "LocalesUtils.hpp" #include #include @@ -750,6 +751,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re std::string path_tmp(path); path_tmp += ".tmp"; + CNumericLocalesSetter c_locales_setter; FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (file == nullptr) throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); @@ -981,6 +983,7 @@ namespace DoExport { double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); while (dst.second < extruder.id()) { // Fill in the non-printing extruders with zeros. dst.first += (dst.second > 0) ? ", 0" : "0"; @@ -1620,7 +1623,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print) int(print.config().machine_max_acceleration_retracting.values.front() + 0.5), travel_acc); - + assert(is_decimal_separator_point()); fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", print.config().machine_max_jerk_x.values.front(), print.config().machine_max_jerk_y.values.front(), @@ -1984,6 +1987,7 @@ void GCode::process_layer( } std::string gcode; + assert(is_decimal_separator_point()); // for the sprintfs // add tag for processor #if ENABLE_VALIDATE_CUSTOM_GCODE @@ -2826,6 +2830,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); char buf[64]; + assert(is_decimal_separator_point()); if (path.role() != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = path.role(); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 917f84f40b..61c835a4b1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1,6 +1,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "libslic3r/Print.hpp" +#include "libslic3r/LocalesUtils.hpp" #include "GCodeProcessor.hpp" #include @@ -465,9 +466,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) }; auto format_time_float = [](float time) { - char time_str[64]; - sprintf(time_str, "%.2f", time); - return std::string(time_str); + return Slic3r::float_to_string_decimal_point(time, 2); }; auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) { diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index fc6a15b656..5e1937ad84 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -4,9 +4,12 @@ #include #include #include +#include +#include #include "GCodeProcessor.hpp" #include "BoundingBox.hpp" +#include "LocalesUtils.hpp" namespace Slic3r @@ -30,29 +33,27 @@ public: m_filpar(filament_parameters) { // adds tag for analyzer: - char buf[64]; + std::ostringstream str; #if ENABLE_VALIDATE_CUSTOM_GCODE - sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; - sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); + str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming + str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << ExtrusionEntity::role_to_string(erWipeTower) << "\n"; #else - sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - m_gcode += buf; - sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str()); + str << ";" << GCodeProcessor::Height_Tag << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming + str << ";" << GCodeProcessor::Extrusion_Role_Tag << ExtrusionEntity::role_to_string(erWipeTower) << "\n"; #endif // ENABLE_VALIDATE_CUSTOM_GCODE - m_gcode += buf; + m_gcode += str.str(); change_analyzer_line_width(line_width); } WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: - char buf[64]; + std::stringstream str; #if ENABLE_VALIDATE_CUSTOM_GCODE - sprintf(buf, ";%s%f\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width).c_str(), line_width); + str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) << line_width << "\n"; #else - sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width); + str << ";" << GCodeProcessor::Width_Tag << line_width << "\n"; #endif // ENABLE_VALIDATE_CUSTOM_GCODE - m_gcode += buf; + m_gcode += str.str(); return *this; } @@ -61,9 +62,9 @@ public: static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); // adds tag for processor: - char buf[64]; - sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); - m_gcode += buf; + std::stringstream str; + str << ";" << GCodeProcessor::Mm3_Per_Mm_Tag << mm3_per_mm << "\n"; + m_gcode += str.str(); return *this; } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -325,9 +326,7 @@ public: { if (time==0.f) return *this; - char buf[128]; - sprintf(buf, "G4 S%.3f\n", time); - m_gcode += buf; + m_gcode += "G4 S" + Slic3r::float_to_string_decimal_point(time, 3) + "\n"; return *this; } @@ -443,37 +442,29 @@ private: const std::vector& m_filpar; std::string set_format_X(float x) - { - char buf[64]; - sprintf(buf, " X%.3f", x); - m_current_pos.x() = x; - return buf; + { + m_current_pos.x() = x; + return " X" + Slic3r::float_to_string_decimal_point(x, 3); } std::string set_format_Y(float y) { - char buf[64]; - sprintf(buf, " Y%.3f", y); - m_current_pos.y() = y; - return buf; + m_current_pos.y() = y; + return " Y" + Slic3r::float_to_string_decimal_point(y, 3); } std::string set_format_Z(float z) { - char buf[64]; - sprintf(buf, " Z%.3f", z); - return buf; + return " Z" + Slic3r::float_to_string_decimal_point(z, 3); } std::string set_format_E(float e) { - char buf[64]; - sprintf(buf, " E%.4f", e); - return buf; + return " E" + Slic3r::float_to_string_decimal_point(e, 4); } std::string set_format_F(float f) { - char buf[64]; - sprintf(buf, " F%d", int(floor(f + 0.5f))); - m_current_feedrate = f; - return buf; + char buf[64]; + sprintf(buf, " F%d", int(floor(f + 0.5f))); + m_current_feedrate = f; + return buf; } WipeTowerWriter& operator=(const WipeTowerWriter &rhs); @@ -959,8 +950,8 @@ void WipeTower::toolchange_Change( // postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before. Vec2f current_pos = writer.pos_rotated(); writer.feedrate(m_travel_speed * 60.f) // see https://github.com/prusa3d/PrusaSlicer/issues/5483 - .append(std::string("G1 X") + std::to_string(current_pos.x()) - + " Y" + std::to_string(current_pos.y()) + .append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(current_pos.x()) + + " Y" + Slic3r::float_to_string_decimal_point(current_pos.y()) + never_skip_tag() + "\n"); // The toolchange Tn command will be inserted later, only in case that the user does @@ -1310,11 +1301,10 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first, { assert(first.new_tool == second.initial_tool); WipeTower::ToolChangeResult out = first; - if (first.end_pos != second.start_pos) { - char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos. - sprintf(buf, "G1 X%.3f Y%.3f F7200\n", second.start_pos.x(), second.start_pos.y()); - out.gcode += buf; - } + if (first.end_pos != second.start_pos) + out.gcode += "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3) + + " Y" + Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3) + + " F7200\n"; out.gcode += second.gcode; out.extrusions.insert(out.extrusions.end(), second.extrusions.begin(), second.extrusions.end()); out.end_pos = second.end_pos; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 52df39fd5f..c35172752b 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1457,7 +1457,7 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config) config.set_key_value("filament_diameter", opt_filam); auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) { - char buf[64]; + char buf[64]; // locales don't matter here (sprintf/atof) sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4); config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false)); }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 209679c0fc..f71921becd 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -5,6 +5,7 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/LocalesUtils.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" #include "Plater.hpp" @@ -78,6 +79,7 @@ static float round_to_nearest(float value, unsigned int decimals) res = std::round(value); else { char buf[64]; + // locales should not matter, both sprintf and stof are sensitive, so... sprintf(buf, "%.*g", decimals, value); res = std::stof(buf); } @@ -3465,18 +3467,16 @@ void GCodeViewer::render_statistics() const ImGuiWrapper& imgui = *wxGetApp().imgui(); auto add_time = [this, &imgui](const std::string& label, int64_t time) { - char buf[1024]; - sprintf(buf, "%lld ms (%s)", time, get_time_dhms(static_cast(time) * 0.001f).c_str()); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); - imgui.text(buf); + imgui.text(std::to_string(time) + " ms (" + get_time_dhms(static_cast(time) * 0.001f) + ")"); }; auto add_memory = [this, &imgui](const std::string& label, int64_t memory) { - auto format_string = [memory](const std::string& units, float value) { - char buf[1024]; - sprintf(buf, "%lld bytes (%.3f %s)", memory, static_cast(memory) * value, units.c_str()); - return std::string(buf); + auto format_string = [memory](const std::string& units, float value) { + return std::to_string(memory) + " bytes (" + + Slic3r::float_to_string_decimal_point(float(memory) * value, 3) + + " " + units + ")"; }; static const float kb = 1024.0f; @@ -3496,11 +3496,9 @@ void GCodeViewer::render_statistics() const }; auto add_counter = [this, &imgui](const std::string& label, int64_t counter) { - char buf[1024]; - sprintf(buf, "%lld", counter); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); ImGui::SameLine(offset); - imgui.text(buf); + imgui.text(std::to_string(counter)); }; imgui.set_next_window_pos(0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width(), 0.0f, ImGuiCond_Once, 0.5f, 0.0f); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 803ab5a143..d6520335bc 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1586,7 +1586,7 @@ bool GUI_App::load_language(wxString language, bool initial) m_wxLocale->AddCatalog(SLIC3R_APP_KEY); m_imgui->set_language(into_u8(language_info->CanonicalName)); //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. - wxSetlocale(LC_NUMERIC, "C"); + //wxSetlocale(LC_NUMERIC, "C"); Preset::update_suffix_modified((" (" + _L("modified") + ")").ToUTF8().data()); return true; } From fef385cd6b948cccb2aa75cb77d0abeb4fcac355 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 May 2021 07:08:38 +0200 Subject: [PATCH 20/28] Fixed second batch of locale-dependent calls --- src/libslic3r/Arrange.cpp | 1 - src/libslic3r/Format/3mf.cpp | 12 +++++ src/libslic3r/Format/AMF.cpp | 53 +++++++++-------------- src/libslic3r/Format/AMF.hpp | 2 +- src/libslic3r/Format/objparser.cpp | 8 ++++ src/libslic3r/GCode/GCodeProcessor.cpp | 8 ++-- src/libslic3r/GCode/PressureEqualizer.cpp | 3 +- src/libslic3r/GCodeReader.cpp | 4 ++ src/libslic3r/LocalesUtils.cpp | 17 ++++++++ src/libslic3r/LocalesUtils.hpp | 2 +- src/slic3r/GUI/Field.cpp | 4 +- src/slic3r/GUI/GUI.cpp | 4 +- src/slic3r/GUI/Selection.cpp | 5 ++- 13 files changed, 78 insertions(+), 45 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 61a32678b9..9602ebdbd1 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 152d72079f..b98cc5f047 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 1c9b6b27d5..94318a930a 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 3e33d4aa37..b4e2c54ab2 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 12cee350dc..6cbef7b779 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 61c835a4b1..f4b38e2ec6 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 c3f084a24a..48a16a8d58 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 9753e48206..5ab1ae50cd 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 d98145e4b6..5ee6ac58ea 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 74281a1575..c74c8b1ac7 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 1fe28eb34a..6775770547 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 f3a8f6ba1a..f4dbcfc437 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 4cf71363d8..5c34d7de0f 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); From c5c6f51ae0802bf39ef4fe2cf3bd324967e30cb1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 May 2021 08:13:23 +0200 Subject: [PATCH 21/28] Fixed third batch of locale-dependent calls --- src/libslic3r/AppConfig.cpp | 15 +++++++++------ src/libslic3r/AppConfig.hpp | 2 +- src/libslic3r/Config.cpp | 4 +++- src/libslic3r/Config.hpp | 8 ++++---- src/libslic3r/Format/SL1.cpp | 1 + src/libslic3r/LocalesUtils.cpp | 8 ++++---- src/libslic3r/LocalesUtils.hpp | 2 +- src/libslic3r/Point.hpp | 10 ++++++---- src/libslic3r/SLAPrintSteps.cpp | 4 ++-- src/slic3r/GUI/BitmapCache.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 11 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index d9301d1f3d..cf532160dc 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -2,8 +2,10 @@ #include "libslic3r/Utils.hpp" #include "AppConfig.hpp" #include "Exception.hpp" +#include "LocalesUtils.hpp" #include "Thread.hpp" + #include #include #include @@ -376,7 +378,8 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje } } -void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed, bool swap_yz) +void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, + float rotation_speed, float rotation_deadzone, double zoom_speed, bool swap_yz) { std::string key = std::string("mouse_device:") + name; auto it = m_storage.find(key); @@ -384,11 +387,11 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe it = m_storage.insert(std::map>::value_type(key, std::map())).first; it->second.clear(); - it->second["translation_speed"] = std::to_string(translation_speed); - it->second["translation_deadzone"] = std::to_string(translation_deadzone); - it->second["rotation_speed"] = std::to_string(rotation_speed); - it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); - it->second["zoom_speed"] = std::to_string(zoom_speed); + it->second["translation_speed"] = float_to_string_decimal_point(translation_speed); + it->second["translation_deadzone"] = float_to_string_decimal_point(translation_deadzone); + it->second["rotation_speed"] = float_to_string_decimal_point(rotation_speed); + it->second["rotation_deadzone"] = float_to_string_decimal_point(rotation_deadzone); + it->second["zoom_speed"] = float_to_string_decimal_point(zoom_speed); it->second["swap_yz"] = swap_yz ? "1" : "0"; } diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index c8ccd18cd8..03924175d8 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -196,6 +196,6 @@ private: bool m_legacy_datadir; }; -}; // namespace Slic3r +} // namespace Slic3r #endif /* slic3r_AppConfig_hpp_ */ diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 5db1d8179b..bd396243c0 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1,6 +1,8 @@ #include "Config.hpp" #include "format.hpp" #include "Utils.hpp" +#include "LocalesUtils.hpp" + #include #include #include @@ -462,7 +464,7 @@ void ConfigBase::set(const std::string &opt_key, double value, bool create) switch (opt->type()) { case coFloat: static_cast(opt)->value = value; break; case coFloatOrPercent: static_cast(opt)->value = value; static_cast(opt)->percent = false; break; - case coString: static_cast(opt)->value = std::to_string(value); break; + case coString: static_cast(opt)->value = float_to_string_decimal_point(value); break; default: throw BadOptionTypeException("Configbase::set() - conversion from float not possible"); } } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index adda2654ed..e67b6b781f 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1819,10 +1819,10 @@ public: SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {} SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} SetDeserializeItem(const std::string &opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} - SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} - SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} - SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} - SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {} + SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {} + SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {} + SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {} + SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(float_to_string_decimal_point(value)), append(append) {} std::string opt_key; std::string opt_value; bool append = false; }; // May throw BadOptionTypeException() if the operation fails. diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 4038cb0467..1809c3e4f3 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -345,6 +345,7 @@ std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key) void fill_iniconf(ConfMap &m, const SLAPrint &print) { + CNumericLocalesSetter locales_setter; // for to_string auto &cfg = print.full_print_config(); m["layerHeight"] = get_cfg_value(cfg, "layer_height"); m["expTime"] = get_cfg_value(cfg, "exposure_time"); diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index 5ee6ac58ea..116078a25b 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -66,10 +66,10 @@ std::string float_to_string_decimal_point(double value, int precision/* = -1*/) return buf.str(); } -std::string float_to_string_decimal_point(float value, int precision/* = -1*/) -{ - return float_to_string_decimal_point(double(value), precision); -} +//std::string float_to_string_decimal_point(float value, int precision/* = -1*/) +//{ +// return float_to_string_decimal_point(double(value), precision); +//} } // namespace Slic3r diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp index c74c8b1ac7..113cfa04b0 100644 --- a/src/libslic3r/LocalesUtils.hpp +++ b/src/libslic3r/LocalesUtils.hpp @@ -39,7 +39,7 @@ bool is_decimal_separator_point(); // to be sure that decimal point is used as a separator. // (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); +//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/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 12870b7132..e5ce68420e 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -11,6 +11,8 @@ #include +#include "LocalesUtils.hpp" + namespace Slic3r { class BoundingBox; @@ -88,10 +90,10 @@ inline Vec3d unscale(coord_t x, coord_t y, coord_t z) { return Vec3d(unscale(pt(0)), unscale(pt(1)), unscale(pt(2))); } inline Vec3d unscale(const Vec3d &pt) { return Vec3d(unscale(pt(0)), unscale(pt(1)), unscale(pt(2))); } -inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } -inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; } -inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; } -inline std::string to_string(const Vec3d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; } +inline std::string to_string(const Vec2crd &pt) { return std::string("[") + float_to_string_decimal_point(pt(0)) + ", " + float_to_string_decimal_point(pt(1)) + "]"; } +inline std::string to_string(const Vec2d &pt) { return std::string("[") + float_to_string_decimal_point(pt(0)) + ", " + float_to_string_decimal_point(pt(1)) + "]"; } +inline std::string to_string(const Vec3crd &pt) { return std::string("[") + float_to_string_decimal_point(pt(0)) + ", " + float_to_string_decimal_point(pt(1)) + ", " + float_to_string_decimal_point(pt(2)) + "]"; } +inline std::string to_string(const Vec3d &pt) { return std::string("[") + float_to_string_decimal_point(pt(0)) + ", " + float_to_string_decimal_point(pt(1)) + ", " + float_to_string_decimal_point(pt(2)) + "]"; } std::vector transform(const std::vector& points, const Transform3f& t); Pointf3s transform(const Pointf3s& points, const Transform3d& t); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 46064c55b8..51e2430aae 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -166,8 +166,8 @@ struct FaceHash { Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3; // Return a concatenated string representation of the coordinates - return std::to_string(c(0)) + std::to_string(c(1)) + std::to_string(c(2)); - }; + return float_to_string_decimal_point(c(0)) + float_to_string_decimal_point(c(1)) + float_to_string_decimal_point(c(2)); + } FaceHash(const indexed_triangle_set &its) { diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 0a6f3e4f9f..2c756c3b95 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -266,7 +266,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ std::string bitmap_key = bitmap_name + ( target_height !=0 ? "-h" + std::to_string(target_height) : "-w" + std::to_string(target_width)) - + (m_scale != 1.0f ? "-s" + std::to_string(m_scale) : "") + + (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "") + (grayscale ? "-gs" : ""); /* For the Dark mode of any platform, we should draw icons in respect to OS background diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 734bc24aa6..455eb8a781 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3800,7 +3800,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) if (imgui->slider_float(_L("Spacing"), &settings.distance, dist_min, 100.0f, "%5.2f") || dist_min > settings.distance) { settings.distance = std::max(dist_min, settings.distance); settings_out.distance = settings.distance; - appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance)); + appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance)); settings_changed = true; } @@ -3815,7 +3815,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) if (imgui->button(_L("Reset"))) { settings_out = ArrangeSettings{}; settings_out.distance = std::max(dist_min, settings_out.distance); - appcfg->set("arrange", dist_key.c_str(), std::to_string(settings_out.distance)); + appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance)); appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0"); settings_changed = true; } From 6b6b0869485ab0c130b3a6cf9c03fa42e291c7d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 May 2021 12:18:30 +0200 Subject: [PATCH 22/28] Fixed tests and windows build --- src/libslic3r/LocalesUtils.cpp | 2 +- tests/libslic3r/test_config.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index 116078a25b..cb118f99ce 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -24,7 +24,7 @@ CNumericLocalesSetter::CNumericLocalesSetter() CNumericLocalesSetter::~CNumericLocalesSetter() { #ifdef _WIN32 - std::setlocale(LC_NUMERIC, m_orig_numeric_locale) + std::setlocale(LC_NUMERIC, m_orig_numeric_locale.data()); #else uselocale(m_original_locale); freelocale(m_new_locale); diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index c3f17def20..417a29dcaf 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -1,6 +1,7 @@ #include #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/LocalesUtils.hpp" using namespace Slic3r; @@ -112,7 +113,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") { WHEN("A string option is set through the double interface") { config.set("end_gcode", 100.5); THEN("The underlying value is set correctly.") { - REQUIRE(config.opt("end_gcode")->value == std::to_string(100.5)); + REQUIRE(config.opt("end_gcode")->value == float_to_string_decimal_point(100.5)); } } WHEN("A float or percent is set as a percent through the string interface.") { From 4a7f07852741d8109d74b67eddaa7d3027028301 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 May 2021 16:40:00 +0200 Subject: [PATCH 23/28] Fixed debug build --- src/libslic3r/Format/objparser.cpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp index 6cbef7b779..16a3f84ddb 100644 --- a/src/libslic3r/Format/objparser.cpp +++ b/src/libslic3r/Format/objparser.cpp @@ -17,7 +17,7 @@ static bool obj_parseline(const char *line, ObjData &data) if (*line == 0) return true; - assert(is_decimal_separator_point()); + assert(Slic3r::is_decimal_separator_point()); // Ignore whitespaces at the beginning of the line. //FIXME is this a good idea? diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 07b9b07bbc..6dd5e7e270 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -357,6 +357,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: for (; *c == ' ' || *c == '\t'; ++ c); if (*c == 0 || *c == ';') break; + + assert(is_decimal_separator_point()); // for atof // Parse the axis. size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') : (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1); @@ -454,6 +456,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_G4; size_t pos_S = sline.find('S', 3); size_t pos_P = sline.find('P', 3); + assert(is_decimal_separator_point()); // for atof line.time = line.time_max = float( (pos_S > 0) ? atof(sline.c_str() + pos_S + 1) : (pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.); From c05b8210f24c6d127778a8762ba1d5af1411e7fd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 24 May 2021 12:20:55 +0200 Subject: [PATCH 24/28] Fixed several locales issues on file import/export --- src/admesh/shared.cpp | 6 +++++- src/admesh/stlinit.cpp | 3 +++ src/slic3r/GUI/GCodeViewer.cpp | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index 902bbfc9b8..a8fbc2a87d 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -30,6 +30,8 @@ #include "stl.h" +#include "libslic3r/LocalesUtils.hpp" + void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) { // 3 indices to vertex per face @@ -127,6 +129,7 @@ void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) bool its_write_off(const indexed_triangle_set &its, const char *file) { + Slic3r::CNumericLocalesSetter locales_setter; /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { @@ -146,6 +149,7 @@ bool its_write_off(const indexed_triangle_set &its, const char *file) bool its_write_vrml(const indexed_triangle_set &its, const char *file) { + Slic3r::CNumericLocalesSetter locales_setter; /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { @@ -188,7 +192,7 @@ bool its_write_vrml(const indexed_triangle_set &its, const char *file) bool its_write_obj(const indexed_triangle_set &its, const char *file) { - + Slic3r::CNumericLocalesSetter locales_setter; FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing"; diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 6aa2c44177..29131b1c75 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -32,6 +32,8 @@ #include "stl.h" +#include "libslic3r/LocalesUtils.hpp" + #ifndef SEEK_SET #error "SEEK_SET not defined" #endif @@ -232,6 +234,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first) bool stl_open(stl_file *stl, const char *file) { + Slic3r::CNumericLocalesSetter locales_setter; stl->clear(); FILE *fp = stl_open_count_facets(stl, file); if (fp == nullptr) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index f71921becd..285f070519 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -992,6 +992,9 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const // save materials file boost::filesystem::path mat_filename(filename); mat_filename.replace_extension("mtl"); + + CNumericLocalesSetter locales_setter; + FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w"); if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing"; From 4960b125c54ca4ae59b9143116c6eaed884cca26 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 24 May 2021 12:22:46 +0200 Subject: [PATCH 25/28] Fixed incorrect locales handling in the UI (Field, ObjectManipulation, etc) --- src/slic3r/GUI/ConfigWizard.cpp | 16 ++++++----- src/slic3r/GUI/DoubleSlider.cpp | 4 +-- src/slic3r/GUI/ExtruderSequenceDialog.cpp | 9 +++++-- src/slic3r/GUI/Field.cpp | 33 +++++++++++++++-------- src/slic3r/GUI/GUI_ObjectLayers.cpp | 10 ++++--- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 12 ++++++--- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index c35172752b..f7d16d843b 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1388,17 +1388,21 @@ static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value) { e.Skip(); wxString str = ctrl->GetValue(); - // Replace the first occurence of comma in decimal number. - bool was_replace = str.Replace(",", ".", false) > 0; + + const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; + // Replace the first incorrect separator in decimal number. + bool was_replaced = str.Replace(dec_sep_alt, dec_sep, false) != 0; + double val = 0.0; - if (!str.ToCDouble(&val)) { + if (!str.ToDouble(&val)) { if (val == 0.0) val = def_value; ctrl->SetValue(double_to_string(val)); show_error(nullptr, _L("Invalid numeric input.")); ctrl->SetFocus(); } - else if (was_replace) + else if (was_replaced) ctrl->SetValue(double_to_string(val)); } @@ -1447,12 +1451,12 @@ PageDiameters::PageDiameters(ConfigWizard *parent) void PageDiameters::apply_custom_config(DynamicPrintConfig &config) { double val = 0.0; - diam_nozzle->GetValue().ToCDouble(&val); + diam_nozzle->GetValue().ToDouble(&val); auto *opt_nozzle = new ConfigOptionFloats(1, val); config.set_key_value("nozzle_diameter", opt_nozzle); val = 0.0; - diam_filam->GetValue().ToCDouble(&val); + diam_filam->GetValue().ToDouble(&val); auto * opt_filam = new ConfigOptionFloats(1, val); config.set_key_value("filament_diameter", opt_filam); diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 6d41e3c820..9c280dee69 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -2162,7 +2162,7 @@ static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, bool disable = textctrl->IsEmpty(); if (!disable && min >= 0.0 && max >= 0.0) { double value = -1.0; - if (!textctrl->GetValue().ToCDouble(&value)) // input value couldn't be converted to double + if (!textctrl->GetValue().ToDouble(&value)) // input value couldn't be converted to double disable = true; else disable = value < min - epsilon() || value > max + epsilon(); // is input value is out of valid range ? @@ -2231,7 +2231,7 @@ static double get_value_to_jump(double active_value, double min_z, double max_z, return -1.0; double value = -1.0; - return dlg.GetValue().ToCDouble(&value) ? value : -1.0; + return dlg.GetValue().ToDouble(&value) ? value : -1.0; } void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.cpp b/src/slic3r/GUI/ExtruderSequenceDialog.cpp index e505f14706..8ae5e9f32d 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.cpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp @@ -98,9 +98,14 @@ ExtruderSequenceDialog::ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequ return; } - str.Replace(",", ".", false); + char dec_sep = '.'; + if (! is_decimal_separator_point()) { + str.Replace(".", ",", false); + dec_sep = ','; + } + double val; - if (str == "." || !str.ToCDouble(&val) || val <= 0.0) + if (str == dec_sep || !str.ToDouble(&val) || val <= 0.0) val = 3.0; // default value if (fabs(m_sequence.interval_by_layers - val) < 0.001) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 6775770547..de8d3c1047 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -37,12 +37,13 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) // with the exception that here one sets the decimal separator explicitely to dot. // If number is in scientific format, trailing zeroes belong to the exponent and cannot be removed. if (s.find_first_of("eE") == wxString::npos) { - const size_t posDecSep = s.find("."); + char dec_sep = is_decimal_separator_point() ? '.' : ','; + const size_t posDecSep = s.find(dec_sep); // No decimal point => removing trailing zeroes irrelevant for integer number. if (posDecSep != wxString::npos) { // Find the last character to keep. size_t posLastNonZero = s.find_last_not_of("0"); - // If it's the decimal separator itself, don't keep it neither. + // If it's the decimal separator itself, don't keep it either. if (posLastNonZero == posDecSep) -- posLastNonZero; s.erase(posLastNonZero + 1); @@ -236,16 +237,22 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true m_value = double(m_opt.min); break; } - double val; - // Replace the first occurence of comma in decimal number. - str.Replace(",", ".", false); - if (str == ".") + double val; + + const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; + // Replace the first incorrect separator in decimal number. + if (str.Replace(dec_sep_alt, dec_sep, false) != 0) + set_value(str, false); + + + if (str == dec_sep) val = 0.0; else { if (m_opt.nullable && str == na_value()) val = ConfigOptionFloatsNullable::nil_value(); - else if (!str.ToCDouble(&val)) + else if (!str.ToDouble(&val)) { if (!check_value) { m_value.clear(); @@ -293,14 +300,18 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') { double val = 0.; - // Replace the first occurence of comma in decimal number. - str.Replace(",", ".", false); + const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; + // Replace the first incorrect separator in decimal number. + if (str.Replace(dec_sep_alt, dec_sep, false) != 0) + set_value(str, false); + // remove space and "mm" substring, if any exists str.Replace(" ", "", true); str.Replace("m", "", true); - if (!str.ToCDouble(&val)) + if (!str.ToDouble(&val)) { if (!check_value) { m_value.clear(); @@ -1478,7 +1489,7 @@ boost::any& PointCtrl::get_value() !y_textctrl->GetValue().ToDouble(&y)) { set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); - show_error(m_parent, _L("Invalid numeric input.")); + show_error(m_parent, _L("Invalid numeric input.")); } else if (m_opt.min > x || x > m_opt.max || diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index d079a6ebdb..7c13c20d67 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -397,12 +397,16 @@ coordf_t LayerRangeEditor::get_value() wxString str = GetValue(); coordf_t layer_height; - // Replace the first occurence of comma in decimal number. - str.Replace(",", ".", false); + const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; + // Replace the first incorrect separator in decimal number. + if (str.Replace(dec_sep_alt, dec_sep, false) != 0) + SetValue(str); + if (str == ".") layer_height = 0.0; else { - if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) { + if (!str.ToDouble(&layer_height) || layer_height < 0.0f) { show_error(m_parent, _L("Invalid numeric input.")); SetValue(double_to_string(layer_height)); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 095a926ad5..cdffbc88da 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1095,15 +1095,19 @@ double ManipulationEditor::get_value() wxString str = GetValue(); double value; - // Replace the first occurence of comma in decimal number. - str.Replace(",", ".", false); + const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; + // Replace the first incorrect separator in decimal number. + if (str.Replace(dec_sep_alt, dec_sep, false) != 0) + SetValue(str); + if (str == ".") value = 0.0; - if ((str.IsEmpty() || !str.ToCDouble(&value)) && !m_valid_value.IsEmpty()) { + if ((str.IsEmpty() || !str.ToDouble(&value)) && !m_valid_value.IsEmpty()) { str = m_valid_value; SetValue(str); - str.ToCDouble(&value); + str.ToDouble(&value); } return value; From afca744d37f1c6fc38a9e143b9d65419f1ae8c2b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 24 May 2021 09:33:27 +0200 Subject: [PATCH 26/28] Fixed a bug when reverting to an old value with comma-separated doubles --- src/slic3r/GUI/GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index f4dbcfc437..c6cb05fac7 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -115,7 +115,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt str.pop_back(); percent = true; } - double val = string_to_double_decimal_point(str); + double val = std::stod(str); // locale-dependent (on purpose - the input is the actual content of the field) config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); break;} case coPercent: From 9d216104e080e17cfb311683b50ff10aa311ce05 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 May 2021 14:42:57 +0200 Subject: [PATCH 27/28] Fixed warning --- src/libslic3r/Geometry.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index e9fc22ab03..cef645e7a4 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -203,19 +203,19 @@ namespace Slic3r { namespace Geometry { static bool sort_points(const Point& a, const Point& b) { - return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1)); + return (a.x() < b.x()) || (a.x() == b.x() && a.y() < b.y()); } static bool sort_pointfs(const Vec3d& a, const Vec3d& b) { - return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1)); + return (a.x() < b.x()) || (a.x() == b.x() && a.y() < b.y()); } // This implementation is based on Andrew's monotone chain 2D convex hull algorithm Polygon convex_hull(Points pts) { - std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); - pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); + std::sort(pts.begin(), pts.end(), sort_points); + pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() == b.x() && a.y() == b.y(); }), pts.end()); Polygon hull; int n = (int)pts.size(); From 671c2ace17cc01d3959ef11973805fb756a4fbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 25 May 2021 10:38:29 +0200 Subject: [PATCH 28/28] Fixed a modification time for files appended using the streaming interface in the miniz library. --- src/miniz/miniz.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/miniz/miniz.c b/src/miniz/miniz.c index cff9aa5d38..d7abeaf3b9 100644 --- a/src/miniz/miniz.c +++ b/src/miniz/miniz.c @@ -6766,6 +6766,12 @@ mz_bool mz_zip_writer_add_staged_open(mz_zip_archive* pZip, mz_zip_writer_staged { mz_zip_time_t_to_dos_time(*pFile_time, &pContext->dos_time, &pContext->dos_date); } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &pContext->dos_time, &pContext->dos_date); + } #endif if (!mz_zip_writer_write_zeros(pZip, pContext->cur_archive_file_ofs, num_alignment_padding_bytes)) @@ -6897,7 +6903,7 @@ mz_bool mz_zip_writer_add_staged_finish(mz_zip_writer_staged_context *pContext) pContext->pExtra_data = pContext->extra_data; pContext->extra_size = mz_zip_writer_create_zip64_extra_data(pContext->extra_data, &pContext->uncomp_size, &pContext->comp_size, (pContext->local_dir_header_ofs >= MZ_UINT32_MAX) ? &pContext->local_dir_header_ofs : NULL); - mz_uint8 min_version[2] = {0x2D, 0x00}; + mz_uint8 min_version[2] = {0x2D, 0x00}; mz_uint8 comp_uncomp_size[4] = {0xFF, 0xFF, 0xFF, 0xFF}; if (pContext->pZip->m_pWrite(pContext->pZip->m_pIO_opaque, local_dir_header_ofs + MZ_ZIP_LDH_VERSION_NEEDED_OFS, min_version, sizeof(min_version)) != sizeof(min_version)) return mz_zip_set_error(pContext->pZip, MZ_ZIP_FILE_WRITE_FAILED);