From 553cd7ad757c34d1ceaa3bc691f899e92c7f6203 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 e27766fb861..ea9eb5ff296 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 fb262eaf93d4dbdf53b5707dc805f71c64033e7c 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 5e18e43ef67..c81f9066ede 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 757b34f51fe290677bc7ba688aa6660736bc402e 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 c81f9066ede..8648edc528a 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 29a42eab3a8..49b11465e98 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 4286abe21138c246d7689945b4796c992c76efc9 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 f916fbdcd68..21b13cbc2b8 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 0649a231f70c6cf15cbb4e631a8b8c8987632392 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 ce4c18a53cc..209679c0fcf 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 38115f181a1540bbc7800ae3ff105c875a9785c7 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 fb41ef93b57..d6d3686d987 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 79e37945dbfeb94c03a927d2a7adb6f84ee3fb87 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 1b29b7f7657..921f9f2b153 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 8df8ed291feb86a62d8fd4dc729531a5dc04fff5 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 570bca41bc4..8d2a79f2a47 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 8648edc528a..fba1429ea2a 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 16db1370e038f4eb649a754ae56029ec76a37d51 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 ea3bec13110..3829ab0d150 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 a5c010d959b..f7493f15729 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 e306641a96e..3aff82fb448 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 5f81eb683319a596444a033d8fde73e17fd7e18a 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 3829ab0d150..734bc24aa60 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 6944107782cca0fe373a33cd6872ae86918753a7 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 08126f441a5..cff9aa5d38c 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 eaf065504c275f4b06f5cd8ce21056ca8557ae47 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 ea9eb5ff296..a3123ea7601 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 83bbf5b56f88551d55e7fbfa7e7c81eeb66a326c 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 d6d3686d987..c7c30daf6bd 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 57189517f406baf9a1262fb2be863bb52c2fe37d 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 c7c30daf6bd..e865b1d78c8 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 4acd20de9d1bbccec322e1238af936582ac5b4e6 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 e865b1d78c8..d988deffba7 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 a59811736a411f70904d258ad63f86d502e1b9af 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 d988deffba7..666db58dce5 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 0cdd866199635c972b149391e1f91c6b39b2c8a5 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 666db58dce5..d0f459e4d47 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 30d15de95146e84bf65b0c3b4faf01690b7399b6 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 d98d0e10fd0..b7b90a8f593 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 00000000000..d98145e4b6a --- /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 00000000000..74281a1575f --- /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 1cb432a4ae6246f91e27d5137027ec1d31d50a9d 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 d47d185a09f..208363f3513 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 917f84f40b9..61c835a4b1b 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 fc6a15b6566..5e1937ad847 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 52df39fd5f6..c35172752b4 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 209679c0fcf..f71921becd5 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 803ab5a143a..d6520335bc9 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 183a702be0d58841f94cf93a0a928d0bc489e2bf 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 61a32678b9d..9602ebdbd13 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 152d72079f9..b98cc5f0474 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 1c9b6b27d5d..94318a930ae 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 3e33d4aa37b..b4e2c54ab2c 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 12cee350dc4..6cbef7b7796 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 61c835a4b1b..f4b38e2ec67 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 c3f084a24ab..48a16a8d58e 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 9753e482063..5ab1ae50cdb 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 d98145e4b6a..5ee6ac58ea8 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 74281a1575f..c74c8b1ac7e 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 1fe28eb34ae..67757705472 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 f3a8f6ba1a7..f4dbcfc437b 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 4cf71363d85..5c34d7de0fa 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 e2b0623f8e995e75821ee02de30c4b41c4dd4651 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 d9301d1f3dd..cf532160dc1 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 c8ccd18cd87..03924175d86 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 5db1d8179bf..bd396243c03 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 adda2654eda..e67b6b781fd 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 4038cb04675..1809c3e4f34 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 5ee6ac58ea8..116078a25bf 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 c74c8b1ac7e..113cfa04b06 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 12870b71329..e5ce68420eb 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 46064c55b8a..51e2430aaeb 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 0a6f3e4f9f6..2c756c3b95b 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 734bc24aa60..455eb8a7812 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 c4f422bbd99cc48ec69c4b802fa76459f043ff8e 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 116078a25bf..cb118f99ce3 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 c3f17def202..417a29dcaf5 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 17d1a61ef45e00251d2e1033410c41e602898bb6 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 6cbef7b7796..16a3f84ddb0 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 07b9b07bbc6..6dd5e7e270e 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 d125c498b59aa78ef3ea0bf384fce6ceb6e9aa82 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 902bbfc9b87..a8fbc2a87db 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 6aa2c441771..29131b1c75b 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 f71921becd5..285f0705190 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 d4ca1d4297d8f2baded80c050eaa1f93f07c64d2 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 c35172752b4..f7d16d843b0 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 6d41e3c8202..9c280dee697 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 e505f14706b..8ae5e9f32dd 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 67757705472..de8d3c10471 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 d079a6ebdb0..7c13c20d678 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 095a926ad57..cdffbc88da1 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 db9c6eeb0ebccd3e8dfaf6c51c3521c65ed04213 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 f4dbcfc437b..c6cb05fac7e 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 4e48f70117051596065c5d1076422b18220265cd 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 e9fc22ab036..cef645e7a40 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 1deeb3aa07e4bdf7ca88b7f963ec75e4d10c9ef7 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 cff9aa5d38c..d7abeaf3b9e 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);