From e3cdeda67324a4f2ff4edc8d8d14ecc2e8642749 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 22 Jun 2021 09:21:16 +0200 Subject: [PATCH 001/240] Add quadric edge collapse --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/QuadricEdgeCollapse.cpp | 620 ++++++++++++++++++ src/libslic3r/QuadricEdgeCollapse.hpp | 17 + src/libslic3r/SimplifyMeshImpl.hpp | 10 +- tests/libslic3r/test_indexed_triangle_set.cpp | 40 ++ 5 files changed, 687 insertions(+), 2 deletions(-) create mode 100644 src/libslic3r/QuadricEdgeCollapse.cpp create mode 100644 src/libslic3r/QuadricEdgeCollapse.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index fe09e6557a..d04c082727 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -177,6 +177,8 @@ add_library(libslic3r STATIC PrintRegion.cpp PNGReadWrite.hpp PNGReadWrite.cpp + QuadricEdgeCollapse.cpp + QuadricEdgeCollapse.hpp Semver.cpp ShortestPath.cpp ShortestPath.hpp diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp new file mode 100644 index 0000000000..1974f51013 --- /dev/null +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -0,0 +1,620 @@ +#include "QuadricEdgeCollapse.hpp" +#include +#include "MutablePriorityQueue.hpp" +#include "SimplifyMeshImpl.hpp" + +using namespace Slic3r; + +// only private namespace not neccessary be in hpp +namespace QuadricEdgeCollapse { + using Vertices = std::vector; + using Triangle = stl_triangle_vertex_indices; + using Indices = std::vector; + using SymMat = SimplifyMesh::implementation::SymetricMatrix; + + struct Error + { + float value; + // range(0 .. 2), + unsigned char min_index; + Error(float value, unsigned char min_index): value(value), min_index(min_index) { + assert(min_index < 3); + } + Error() = default; + }; + using Errors = std::vector; + // merge information together - faster access during processing + struct TriangleInfo + { + Vec3f n; // normalized normal - speed up calcualtion of q and check flip + Error e; // smallest error caused by edges, identify smallest edge in triangle + TriangleInfo() = default; + bool is_deleted() const { return e.min_index > 2; } + void set_deleted() { e.min_index = 3; } + }; + using TriangleInfos = std::vector; + struct VertexInfo + { + SymMat q; // sum quadric of surround triangles + size_t start = 0, count = 0; // vertex neighbor triangles + VertexInfo() = default; + bool is_deleted() const { return count == 0; } + }; + using VertexInfos = std::vector; + struct EdgeInfo + { + size_t t_index=0; // triangle index + unsigned char edge = 0; // 0 or 1 or 2 + EdgeInfo() = default; + }; + using EdgeInfos = std::vector; + + Vec3f create_normal(const Triangle &triangle, const Vertices &vertices); + double calculate_error(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices); + Vec3f calculate_vertex(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices); + // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number + double vertex_error(const SymMat &q, const Vec3d &vertex); + SymMat create_quadric(const Triangle &t, const TriangleInfo &t_info, const Vertices &vertices); + std::tuple init(const indexed_triangle_set &its); + size_t find_triangle_index1(size_t vi, const VertexInfo& v_info, size_t ti, const EdgeInfos& e_infos, const Indices& indices); + bool is_flipped(const Vec3f &vn, const Vec3f &v1, const Vec3f &v2, const Vec3f &normal); + bool is_flipped(Vec3f &new_vertex, size_t ti0, size_t ti1, const VertexInfo& v_info, + const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); + + // find edge with smallest error in triangle + Error calculate_error(const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos); + // subtract quadric of one triangle from triangle vertex + void sub_quadric(const Triangle &t, const TriangleInfo &t_info, VertexInfos &v_infos, const Vertices &vertices); + + void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, size_t ti); + void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, size_t ti0, size_t ti1, + size_t vi0, size_t vi1, size_t vi_top0, const Triangle &t1); + + void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its); + } + +using namespace QuadricEdgeCollapse; + +bool check_neighbors(TriangleInfos &t_infos, + Indices& indices, + VertexInfos & v_infos) +{ + std::vector t_counts(v_infos.size(), 0); + for (size_t i = 0; i < indices.size(); i++) { + TriangleInfo &t_info = t_infos[i]; + if (t_info.is_deleted()) continue; + Triangle &t = indices[i]; + for (size_t vidx : t) ++t_counts[vidx]; + } + + size_t prev_end = 0; + for (size_t i = 0; i < v_infos.size(); i++) { + VertexInfo &v_info = v_infos[i]; + if (v_info.is_deleted()) continue; + if (v_info.count != t_counts[i]) { + // no correct count + return false; + } + if (prev_end > v_info.start) { + // overlap of start + return false; + } + prev_end = v_info.start + v_info.count; + } + return true; +} + +bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { + float epsilon = 1.f; + for (size_t i = 0; i < 3; i++) { + if (nv[i] > (v0[i] + epsilon) && nv[i] > (v1[i] + epsilon) || + nv[i] < (v0[i] - epsilon) && nv[i] < (v1[i] - epsilon)) { + return false; + } + } + return true; +} + +bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, + size_t triangle_count) +{ + TriangleInfos t_infos; + VertexInfos v_infos; + EdgeInfos e_infos; + std::tie(t_infos, v_infos, e_infos) = init(its); + + static constexpr double max_error = std::numeric_limits::max(); + + auto cmp = [&t_infos](size_t vi0, size_t vi1) -> bool { + const Error &e0 = t_infos[vi0].e; + const Error &e1 = t_infos[vi1].e; + return e0.value < e1.value; + }; + // convert triangle index to priority queue index + std::vector i_convert(its.indices.size(), {0}); + auto setter = [&i_convert](size_t it, size_t index) { i_convert[it] = index; }; + MutablePriorityQueue mpq(std::move(setter), std::move(cmp)); + mpq.reserve(its.indices.size()); + for (size_t i = 0; i < its.indices.size(); i++) mpq.push(i); + + size_t actual_triangle_count = its.indices.size(); + while (actual_triangle_count > triangle_count && !mpq.empty()) { + // triangle index 0 + size_t ti0 = mpq.top(); + mpq.pop(); + TriangleInfo &t_info0 = t_infos[ti0]; + if (t_info0.is_deleted()) continue; + + Error &e = t_info0.e; + + const Triangle &t0 = its.indices[ti0]; + size_t vi0 = t0[e.min_index]; + size_t vi1 = t0[(e.min_index+1) %3]; + // Need by move of neighbor edge infos in function: change_neighbors + if (vi0 > vi1) std::swap(vi0, vi1); + VertexInfo &v_info0 = v_infos[vi0]; + VertexInfo &v_info1 = v_infos[vi1]; + assert(!v_info0.is_deleted() && !v_info1.is_deleted()); + + // new vertex position + SymMat q(v_info0.q); + q += v_info1.q; + Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); + assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1])); + // set of triangle indices that change quadric + size_t ti1 = (v_info0.count < v_info1.count)? + find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : + find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; + + if (is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) || + is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) { + // IMPROVE1: what about other edges in triangle? + // IMPROVE2: check mpq top if it is ti1 with same edge + e.value = max_error; + mpq.push(ti0); + continue; + } + + std::vector changed_triangle_indices; + changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4); + + sub_quadric(t0, t_info0, v_infos, its.vertices); + TriangleInfo &t_info1 = t_infos[ti1]; + const Triangle &t1 = its.indices[ti1]; + sub_quadric(t1, t_info1, v_infos, its.vertices); + // for each vertex0 triangles + size_t v_info0_end = v_info0.start + v_info0.count; + for (size_t di = v_info0.start; di < v_info0_end; ++di) { + assert(di < e_infos.size()); + EdgeInfo &e_info = e_infos[di]; + size_t ti = e_info.t_index; + if (ti == ti0) continue; // ti0 will be deleted + if (ti == ti1) continue; // ti1 will be deleted + sub_quadric(its.indices[ti], t_infos[ti], v_infos, its.vertices); + changed_triangle_indices.emplace_back(ti); + } + + // for each vertex1 triangles + size_t v_info1_end = v_info1.start + v_info1.count; + for (size_t di = v_info1.start; di < v_info1_end; ++di) { + assert(di < e_infos.size()); + EdgeInfo &e_info = e_infos[di]; + size_t ti = e_info.t_index; + if (ti == ti0) continue; // ti0 will be deleted + if (ti == ti1) continue; // ti1 will be deleted + Triangle &t = its.indices[ti]; + sub_quadric(t, t_infos[ti], v_infos, its.vertices); + t[e_info.edge] = vi0; // change index + changed_triangle_indices.emplace_back(ti); + } + + // fix neighbors + + // vertex index of triangle 0 which is not vi0 nor vi1 + size_t vi_top0 = t0[(e.min_index + 2) % 3]; + change_neighbors(e_infos, v_infos, ti0, ti1, vi0, vi1, vi_top0, t1); + + // Change vertex + // Has to be set after subtract quadric + its.vertices[vi0] = new_vertex0; + + // add new quadrics + v_info0.q = SymMat(); // zero value + for (size_t ti : changed_triangle_indices) { + const Triangle& t = its.indices[ti]; + TriangleInfo &t_info = t_infos[ti]; + t_info.n = create_normal(t, its.vertices); // new normal + SymMat q = create_quadric(t, t_info, its.vertices); + for (const size_t vi: t) v_infos[vi].q += q; + } + + // fix errors - must be after calculate all quadric + t_info1.e.value = max_error; // not neccessary when check deleted triangles at begining + //mpq.remove(i_convert[ti1]); + for (size_t ti : changed_triangle_indices) { + const Triangle &t = its.indices[ti]; + t_infos[ti].e = calculate_error(t, its.vertices, v_infos); + mpq.update(i_convert[ti]); + } + + // set triangle(0 + 1) indices as deleted + t_info0.set_deleted(); + t_info1.set_deleted(); + // triangle counter decrementation + actual_triangle_count-=2; + + //assert(check_neighbors(t_infos, its.indices, v_infos)); + } + + // compact triangle + compact(v_infos, t_infos, e_infos, its); + return true; +} + +Vec3f QuadricEdgeCollapse::create_normal(const Triangle & triangle, + const Vertices &vertices) +{ + const Vec3f &v0 = vertices[triangle[0]]; + const Vec3f &v1 = vertices[triangle[1]]; + const Vec3f &v2 = vertices[triangle[2]]; + // n = triangle normal + Vec3f n = (v1 - v0).cross(v2 - v0); + n.normalize(); + return n; +} + +double QuadricEdgeCollapse::calculate_error(size_t id_v1, + size_t id_v2, + SymMat & q, + const Vertices &vertices) +{ + double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); + if (abs(det) < std::numeric_limits::epsilon()) { + // can't divide by zero + const Vec3f &v0 = vertices[id_v1]; + const Vec3f &v1 = vertices[id_v2]; + Vec3d verts[3] = {v0.cast(), v1.cast(), Vec3d()}; + verts[2] = (verts[0] + verts[1]) / 2; + double errors[] = {vertex_error(q, verts[0]), + vertex_error(q, verts[1]), + vertex_error(q, verts[2])}; + return *std::min_element(std::begin(errors), std::end(errors)); + } + + double det_1 = -1 / det; + double det_x = q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) + double det_y = q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) + double det_z = q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) + Vec3d vertex(det_1 * det_x, -det_1 * det_y, det_1 * det_z); + return vertex_error(q, vertex); +} + +// similar as calculate error but focus on new vertex without calculation of error +Vec3f QuadricEdgeCollapse::calculate_vertex(size_t id_v1, + size_t id_v2, + SymMat & q, + const Vertices &vertices) +{ + double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); + if (abs(det) < std::numeric_limits::epsilon()) { + // can't divide by zero + const Vec3f &v0 = vertices[id_v1]; + const Vec3f &v1 = vertices[id_v2]; + Vec3d verts[3] = {v0.cast(), v1.cast(), Vec3d()}; + verts[2] = (verts[0] + verts[1]) / 2; + double errors[] = {vertex_error(q, verts[0]), + vertex_error(q, verts[1]), + vertex_error(q, verts[2])}; + auto mit = std::min_element(std::begin(errors), std::end(errors)); + return verts[mit - std::begin(errors)].cast(); + } + + double det_1 = -1 / det; + double det_x = q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) + double det_y = q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) + double det_z = q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) + return Vec3f(det_1 * det_x, -det_1 * det_y, det_1 * det_z); +} + +double QuadricEdgeCollapse::vertex_error(const SymMat &q, const Vec3d &vertex) +{ + const double &x = vertex.x(), &y = vertex.y(), &z = vertex.z(); + return q[0] * x * x + 2 * q[1] * x * y + 2 * q[2] * x * z + + 2 * q[3] * x + q[4] * y * y + 2 * q[5] * y * z + + 2 * q[6] * y + q[7] * z * z + 2 * q[8] * z + q[9]; +} + +SymMat QuadricEdgeCollapse::create_quadric(const Triangle & t, + const TriangleInfo &t_info, + const Vertices & vertices) +{ + Vec3d n = t_info.n.cast(); + Vec3d v0 = vertices[t[0]].cast(); + return SymMat(n.x(), n.y(), n.z(), -n.dot(v0)); +} + +std::tuple QuadricEdgeCollapse::init( + const indexed_triangle_set &its) +{ + TriangleInfos t_infos(its.indices.size()); + VertexInfos v_infos(its.vertices.size()); + EdgeInfos e_infos(its.indices.size() * 3); + + for (size_t i = 0; i < its.indices.size(); i++) { + const Triangle &t = its.indices[i]; + TriangleInfo &t_info = t_infos[i]; + t_info.n = create_normal(t, its.vertices); + SymMat q = create_quadric(t, t_info, its.vertices); + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos[t[e]]; + v_info.q += q; + ++v_info.count; // triangle count + } + } + + // set offseted starts + size_t triangle_start = 0; + for (VertexInfo &v_info : v_infos) { + v_info.start = triangle_start; + triangle_start += v_info.count; + // set filled vertex to zero + v_info.count = 0; + } + assert(its.indices.size() * 3 == triangle_start); + + // calc error + create reference + for (size_t i = 0; i < its.indices.size(); i++) { + const Triangle &t = its.indices[i]; + TriangleInfo &t_info = t_infos[i]; + t_info.e = calculate_error(t, its.vertices, v_infos); + for (size_t j = 0; j < 3; ++j) { + VertexInfo &v_info = v_infos[t[j]]; + size_t ei = v_info.start + v_info.count; + assert(ei < e_infos.size()); + EdgeInfo &e_info = e_infos[ei]; + e_info.t_index = i; + e_info.edge = j; + ++v_info.count; + } + } + return {t_infos, v_infos, e_infos}; +} + +size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi, + const VertexInfo &v_info, + size_t ti0, + const EdgeInfos & e_infos, + const Indices &indices) +{ + size_t end = v_info.start + v_info.count; + for (size_t ei = v_info.start; ei < end; ++ei) { + const EdgeInfo &e_info = e_infos[ei]; + if (e_info.t_index == ti0) continue; + const Triangle& t = indices[e_info.t_index]; + if (t[(e_info.edge + 1) % 3] == vi || + t[(e_info.edge + 2) % 3] == vi) + return e_info.t_index; + } + // triangle0 is on border and do NOT have twin edge + assert(false); + return -1; +} + + +bool QuadricEdgeCollapse::is_flipped(const Vec3f &vn, + const Vec3f &v1, + const Vec3f &v2, + const Vec3f &normal) +{ + static const float thr_pos = 1.0f - std::numeric_limits::epsilon(); + static const float thr_neg = -thr_pos; + static const float dot_thr = 0.2f; // Value from simplify mesh + + Vec3f d1 = v1 - vn; + d1.normalize(); + Vec3f d2 = v2 - vn; + d2.normalize(); + + float dot = d1.dot(d2); + if (dot > thr_pos || dot < thr_neg) return true; + + // IMPROVE: propagate new normal + Vec3f n = d1.cross(d2); + n.normalize(); + return n.dot(normal) < dot_thr; +} + + +bool QuadricEdgeCollapse::is_flipped(Vec3f & new_vertex, + size_t ti0, + size_t ti1, + const VertexInfo & v_info, + const TriangleInfos &t_infos, + const EdgeInfos & e_infos, + const indexed_triangle_set &its) +{ + // for each vertex triangles + size_t v_info_end = v_info.start + v_info.count; + for (size_t ei = v_info.start; ei < v_info_end; ++ei) { + assert(ei < e_infos.size()); + const EdgeInfo &e_info = e_infos[ei]; + if (e_info.t_index == ti0) continue; // ti0 will be deleted + if (e_info.t_index == ti1) continue; // ti1 will be deleted + const Triangle &t = its.indices[e_info.t_index]; + const Vec3f &normal = t_infos[e_info.t_index].n; + const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]]; + const Vec3f &vs = its.vertices[t[(e_info.edge + 2) % 3]]; + if (is_flipped(new_vertex, vf, vs, normal)) + return true; + } + return false; +} + +Error QuadricEdgeCollapse::calculate_error(const Triangle & t, + const Vertices & vertices, + const VertexInfos &v_infos) +{ + Vec3d error; + for (size_t j = 0; j < 3; ++j) { + size_t j2 = (j == 2) ? 0 : (j + 1); + size_t vi0 = t[j]; + size_t vi1 = t[j2]; + SymMat q(v_infos[vi0].q); // copy + q += v_infos[vi1].q; + error[j] = calculate_error(vi0, vi1, q, vertices); + } + unsigned char min_index = (error[0] < error[1]) ? + ((error[0] < error[2]) ? 0 : 2) : + ((error[1] < error[2]) ? 1 : 2); + return Error(static_cast(error[min_index]), min_index); +} + +void QuadricEdgeCollapse::sub_quadric(const Triangle &t, + const TriangleInfo & t_info, + VertexInfos &v_infos, + const Vertices &vertices) +{ + SymMat quadric = create_quadric(t, t_info, vertices); + for (size_t vi: t) v_infos[vi].q -= quadric; +} + +void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos, + VertexInfo &v_info, + size_t ti) +{ + auto e_info = e_infos.begin() + v_info.start; + auto e_info_end = e_info + v_info.count - 1; + for (; e_info != e_info_end; ++e_info) { + if (e_info->t_index == ti) { + *e_info = *e_info_end; + --v_info.count; + return; + } + } + assert(e_info_end->t_index == ti); + // last triangle is ti + --v_info.count; +} + +void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, + VertexInfos & v_infos, + size_t ti0, + size_t ti1, + size_t vi0, + size_t vi1, + size_t vi_top0, + const Triangle &t1) +{ + // have to copy Edge info from higher vertex index into smaller + assert(vi0 < vi1); + + // vertex index of triangle 1 which is not vi0 nor vi1 + size_t vi_top1 = t1[0]; + if (vi_top1 == vi0 || vi_top1 == vi1) { + vi_top1 = (t1[1] == vi0 || t1[1] == vi1)? t1[2] : t1[1]; + } + + remove_triangle(e_infos, v_infos[vi_top0], ti0); + remove_triangle(e_infos, v_infos[vi_top1], ti1); + + VertexInfo &v_info0 = v_infos[vi0]; + VertexInfo &v_info1 = v_infos[vi1]; + + size_t new_triangle_count = v_info0.count + v_info1.count - 4; + remove_triangle(e_infos, v_info0, ti0); + remove_triangle(e_infos, v_info0, ti1); + + // copy second's edge infos out of e_infos, to free size + EdgeInfos e_infos1; + e_infos1.reserve(v_info1.count - 2); + size_t v_info_s_end = v_info1.start + v_info1.count; + for (size_t ei = v_info1.start; ei < v_info_s_end; ++ei) { + const EdgeInfo &e_info = e_infos[ei]; + if (e_info.t_index == ti0) continue; + if (e_info.t_index == ti1) continue; + e_infos1.emplace_back(e_info); + } + v_info1.count = 0; + + size_t need = (new_triangle_count < v_info0.count)? 0: + (new_triangle_count - v_info0.count); + + size_t act_vi = vi0 + 1; + VertexInfo *act_v_info = &v_infos[act_vi]; + size_t act_start = act_v_info->start; + size_t last_end = v_info0.start + v_info0.count; + + struct CopyEdgeInfo + { + size_t start; + size_t count; + unsigned char move; + CopyEdgeInfo(size_t start, size_t count, unsigned char move) + : start(start), count(count), move(move) + {} + }; + std::vector c_infos; + c_infos.reserve(need); + while (true) { + size_t save = act_start - last_end; + if (save > 0) { + if (save >= need) break; + need -= save; + c_infos.emplace_back(act_v_info->start, act_v_info->count, need); + } else { + c_infos.back().count += act_v_info->count; + } + last_end = act_v_info->start + act_v_info->count; + act_v_info->start += need; + ++act_vi; + if (act_vi < v_infos.size()) { + act_v_info = &v_infos[act_vi]; + act_start = act_v_info->start; + } else + act_start = e_infos.size(); // fix for edge between last two triangles + } + + // copy by c_infos + for (size_t i = c_infos.size(); i > 0; --i) { + const CopyEdgeInfo &c_info = c_infos[i - 1]; + for (size_t ei = c_info.start + c_info.count - 1; ei >= c_info.start; --ei) + e_infos[ei + c_info.move] = e_infos[ei]; // copy + } + + // copy triangle from first info into second + for (size_t ei_s = 0; ei_s < e_infos1.size(); ++ei_s) { + size_t ei_f = v_info0.start + v_info0.count; + e_infos[ei_f] = e_infos1[ei_s]; // copy + ++v_info0.count; + } +} + +void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, + const TriangleInfos & t_infos, + const EdgeInfos & e_infos, + indexed_triangle_set &its) +{ + size_t vi_new = 0; + for (size_t vi = 0; vi < v_infos.size(); vi++) { + const VertexInfo &v_info = v_infos[vi]; + if (v_info.is_deleted()) continue; // deleted + size_t e_info_end = v_info.start + v_info.count; + for (size_t ei = v_info.start; ei < e_info_end; ei++) { + const EdgeInfo &e_info = e_infos[ei]; + // change vertex index + its.indices[e_info.t_index][e_info.edge] = vi_new; + } + // compact vertices + its.vertices[vi_new++] = its.vertices[vi]; + } + // remove vertices tail + its.vertices.erase(its.vertices.begin() + vi_new, its.vertices.end()); + + size_t ti_new = 0; + for (size_t ti = 0; ti < t_infos.size(); ti++) { + const TriangleInfo &t_info = t_infos[ti]; + if (t_info.is_deleted()) continue; + its.indices[ti_new++] = its.indices[ti]; + } + its.indices.erase(its.indices.begin() + ti_new, its.indices.end()); +} \ No newline at end of file diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp new file mode 100644 index 0000000000..d435656b20 --- /dev/null +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -0,0 +1,17 @@ +// paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf +// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/ +// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification + +#include "TriangleMesh.hpp" + +namespace Slic3r { + +/// +/// Simplify mesh by Quadric metric +/// +/// IN/OUT triangle mesh to be simplified. +/// wanted triangle count. +/// TRUE on success otherwise FALSE +bool its_quadric_edge_collapse(indexed_triangle_set &its, size_t triangle_count); + +} // namespace Slic3r diff --git a/src/libslic3r/SimplifyMeshImpl.hpp b/src/libslic3r/SimplifyMeshImpl.hpp index 4b6b0f5cbc..0b3b9a2f64 100644 --- a/src/libslic3r/SimplifyMeshImpl.hpp +++ b/src/libslic3r/SimplifyMeshImpl.hpp @@ -107,7 +107,7 @@ public: // Determinant T det(int a11, int a12, int a13, int a21, int a22, int a23, - int a31, int a32, int a33) + int a31, int a32, int a33) const { T det = m[a11] * m[a22] * m[a33] + m[a13] * m[a21] * m[a32] + m[a12] * m[a23] * m[a31] - m[a13] * m[a22] * m[a31] - @@ -121,7 +121,13 @@ public: for (size_t i = 0; i < N; ++i) m[i] += n[i]; return *this; } - + + const SymetricMatrix &operator-=(const SymetricMatrix &n) + { + for (size_t i = 0; i < N; ++i) m[i] -= n[i]; + return *this; + } + SymetricMatrix operator+(const SymetricMatrix& n) { SymetricMatrix self = *this; diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 4c11283451..0ec7e3bd05 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -4,6 +4,8 @@ #include "libslic3r/TriangleMesh.hpp" +using namespace Slic3r; + TEST_CASE("Split empty mesh", "[its_split][its]") { using namespace Slic3r; @@ -100,3 +102,41 @@ TEST_CASE("Split two watertight meshes", "[its_split][its]") { debug_write_obj(res, "parts_watertight"); } +#include +TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") +{ + indexed_triangle_set its; + its.vertices = {Vec3f(-1.f, 0.f, 0.f), Vec3f(0.f, 1.f, 0.f), + Vec3f(1.f, 0.f, 0.f), Vec3f(0.f, 0.f, 1.f), + // vertex to be removed + Vec3f(0.9f, .1f, -.1f)}; + its.indices = {Vec3i(1, 0, 3), Vec3i(2, 1, 3), Vec3i(0, 2, 3), + Vec3i(0, 1, 4), Vec3i(1, 2, 4), Vec3i(2, 0, 4)}; + // edge to remove is between vertices 2 and 4 on trinagles 4 and 5 + + indexed_triangle_set its_ = its; // copy + // its_write_obj(its, "tetrhedron_in.obj"); + size_t wanted_count = its.indices.size() - 1; + CHECK(its_quadric_edge_collapse(its, wanted_count)); + // its_write_obj(its, "tetrhedron_out.obj"); + CHECK(its.indices.size() == 4); + CHECK(its.vertices.size() == 4); + + for (size_t i = 0; i < 3; i++) { + CHECK(its.indices[i] == its_.indices[i]); + } + + for (size_t i = 0; i < 4; i++) { + if (i == 2) continue; + CHECK(its.vertices[i] == its_.vertices[i]); + } + + const Vec3f &v = its.vertices[2]; // new vertex + const Vec3f &v2 = its_.vertices[2]; // moved vertex + const Vec3f &v4 = its_.vertices[4]; // removed vertex + for (size_t i = 0; i < 3; i++) { + bool is_between = (v[i] < v4[i] && v[i] > v2[i]) || + (v[i] > v4[i] && v[i] < v2[i]); + CHECK(is_between); + } +} \ No newline at end of file From 7c13cfa1d9f540c0505a9d1d835f21736f0e48be Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 22 Jun 2021 09:41:38 +0200 Subject: [PATCH 002/240] Add test checking diference in volume before/after simplification FIX warnings ..\src\libslic3r\QuadricEdgeCollapse.cpp(173): warning C4056: overflow in floating-point constant arithmetic ..\src\libslic3r\QuadricEdgeCollapse.cpp(232): warning C4056: overflow in floating-point constant arithmetic --- src/libslic3r/QuadricEdgeCollapse.cpp | 2 +- tests/libslic3r/test_indexed_triangle_set.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 1974f51013..13313da1f6 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -123,7 +123,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, EdgeInfos e_infos; std::tie(t_infos, v_infos, e_infos) = init(its); - static constexpr double max_error = std::numeric_limits::max(); + static constexpr float max_error = std::numeric_limits::max(); auto cmp = [&t_infos](size_t vi0, size_t vi1) -> bool { const Error &e0 = t_infos[vi0].e; diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 0ec7e3bd05..ac676267ac 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -139,4 +139,19 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") (v[i] > v4[i] && v[i] < v2[i]); CHECK(is_between); } +} + +#include "test_utils.hpp" +TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") +{ + TriangleMesh mesh = load_model("frog_legs.obj"); + double original_volume = its_volume(mesh.its); + size_t wanted_count = mesh.its.indices.size() * 0.05; + REQUIRE_FALSE(mesh.empty()); + indexed_triangle_set its = mesh.its; // copy + its_quadric_edge_collapse(its, wanted_count); + // its_write_obj(its, "frog_legs_qec.obj"); + CHECK(its.indices.size() <= wanted_count); + double volume = its_volume(its); + CHECK(fabs(original_volume - volume) < 30.); } \ No newline at end of file From 892c246700457160f1c67c9ef74aa83d2ea0ef29 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 22 Jun 2021 10:01:14 +0200 Subject: [PATCH 003/240] Fix Linux warnings ../src/libslic3r/QuadricEdgeCollapse.cpp:110:39: warning: suggest parentheses around '&&' within '||' [-Wparentheses] ../src/libslic3r/QuadricEdgeCollapse.cpp:394:38: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] ../src/libslic3r/QuadricEdgeCollapse.cpp:395:38: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] ../src/libslic3r/QuadricEdgeCollapse.cpp:514:26: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] ../src/libslic3r/QuadricEdgeCollapse.cpp:514:42: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] --- src/libslic3r/QuadricEdgeCollapse.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 13313da1f6..858a6fbbb8 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -107,8 +107,8 @@ bool check_neighbors(TriangleInfos &t_infos, bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { float epsilon = 1.f; for (size_t i = 0; i < 3; i++) { - if (nv[i] > (v0[i] + epsilon) && nv[i] > (v1[i] + epsilon) || - nv[i] < (v0[i] - epsilon) && nv[i] < (v1[i] - epsilon)) { + if ((nv[i] > (v0[i] + epsilon) && nv[i] > (v1[i] + epsilon)) || + (nv[i] < (v0[i] - epsilon) && nv[i] < (v1[i] - epsilon))) { return false; } } @@ -386,13 +386,14 @@ size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi, const EdgeInfos & e_infos, const Indices &indices) { + coord_t vi_coord = static_cast(vi); size_t end = v_info.start + v_info.count; for (size_t ei = v_info.start; ei < end; ++ei) { const EdgeInfo &e_info = e_infos[ei]; if (e_info.t_index == ti0) continue; const Triangle& t = indices[e_info.t_index]; - if (t[(e_info.edge + 1) % 3] == vi || - t[(e_info.edge + 2) % 3] == vi) + if (t[(e_info.edge + 1) % 3] == vi_coord || + t[(e_info.edge + 2) % 3] == vi_coord) return e_info.t_index; } // triangle0 is on border and do NOT have twin edge @@ -508,10 +509,12 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, // have to copy Edge info from higher vertex index into smaller assert(vi0 < vi1); + // vertex index of triangle 1 which is not vi0 nor vi1 size_t vi_top1 = t1[0]; if (vi_top1 == vi0 || vi_top1 == vi1) { - vi_top1 = (t1[1] == vi0 || t1[1] == vi1)? t1[2] : t1[1]; + vi_top1 = t1[1]; + if (vi_top1 == vi0 || vi_top1 == vi1) vi_top1 = t1[2]; } remove_triangle(e_infos, v_infos[vi_top0], ti0); From 1196fac55142c73ebd8f6464bc22e2b8aac66614 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 23 Jun 2021 09:50:27 +0200 Subject: [PATCH 004/240] Parallel QEC initialization --- src/libslic3r/QuadricEdgeCollapse.cpp | 102 ++++++++++++++------------ 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 858a6fbbb8..7244b6c1aa 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -54,17 +54,16 @@ namespace QuadricEdgeCollapse { Vec3f calculate_vertex(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices); // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number double vertex_error(const SymMat &q, const Vec3d &vertex); - SymMat create_quadric(const Triangle &t, const TriangleInfo &t_info, const Vertices &vertices); + SymMat create_quadric(const Triangle &t, const Vec3f& normal, const Vertices &vertices); std::tuple init(const indexed_triangle_set &its); size_t find_triangle_index1(size_t vi, const VertexInfo& v_info, size_t ti, const EdgeInfos& e_infos, const Indices& indices); - bool is_flipped(const Vec3f &vn, const Vec3f &v1, const Vec3f &v2, const Vec3f &normal); - bool is_flipped(Vec3f &new_vertex, size_t ti0, size_t ti1, const VertexInfo& v_info, + bool is_flipped(const Vec3f &new_vertex, size_t ti0, size_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); // find edge with smallest error in triangle Error calculate_error(const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos); // subtract quadric of one triangle from triangle vertex - void sub_quadric(const Triangle &t, const TriangleInfo &t_info, VertexInfos &v_infos, const Vertices &vertices); + void sub_quadric(const Triangle &t, const Vec3f& normal, VertexInfos &v_infos, const Vertices &vertices); void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, size_t ti); void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, size_t ti0, size_t ti1, @@ -178,10 +177,10 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, std::vector changed_triangle_indices; changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4); - sub_quadric(t0, t_info0, v_infos, its.vertices); + sub_quadric(t0, t_info0.n, v_infos, its.vertices); TriangleInfo &t_info1 = t_infos[ti1]; const Triangle &t1 = its.indices[ti1]; - sub_quadric(t1, t_info1, v_infos, its.vertices); + sub_quadric(t1, t_info1.n, v_infos, its.vertices); // for each vertex0 triangles size_t v_info0_end = v_info0.start + v_info0.count; for (size_t di = v_info0.start; di < v_info0_end; ++di) { @@ -190,7 +189,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, size_t ti = e_info.t_index; if (ti == ti0) continue; // ti0 will be deleted if (ti == ti1) continue; // ti1 will be deleted - sub_quadric(its.indices[ti], t_infos[ti], v_infos, its.vertices); + sub_quadric(its.indices[ti], t_infos[ti].n, v_infos, its.vertices); changed_triangle_indices.emplace_back(ti); } @@ -203,7 +202,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, if (ti == ti0) continue; // ti0 will be deleted if (ti == ti1) continue; // ti1 will be deleted Triangle &t = its.indices[ti]; - sub_quadric(t, t_infos[ti], v_infos, its.vertices); + sub_quadric(t, t_infos[ti].n, v_infos, its.vertices); t[e_info.edge] = vi0; // change index changed_triangle_indices.emplace_back(ti); } @@ -224,7 +223,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, const Triangle& t = its.indices[ti]; TriangleInfo &t_info = t_infos[ti]; t_info.n = create_normal(t, its.vertices); // new normal - SymMat q = create_quadric(t, t_info, its.vertices); + SymMat q = create_quadric(t, t_info.n, its.vertices); for (const size_t vi: t) v_infos[vi].q += q; } @@ -325,10 +324,10 @@ double QuadricEdgeCollapse::vertex_error(const SymMat &q, const Vec3d &vertex) } SymMat QuadricEdgeCollapse::create_quadric(const Triangle & t, - const TriangleInfo &t_info, + const Vec3f &normal, const Vertices & vertices) { - Vec3d n = t_info.n.cast(); + Vec3d n = normal.cast(); Vec3d v0 = vertices[t[0]].cast(); return SymMat(n.x(), n.y(), n.z(), -n.dot(v0)); } @@ -339,12 +338,22 @@ std::tuple QuadricEdgeCollapse::init( TriangleInfos t_infos(its.indices.size()); VertexInfos v_infos(its.vertices.size()); EdgeInfos e_infos(its.indices.size() * 3); + + // calculate normals + tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), + [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const Triangle &t = its.indices[i]; + TriangleInfo & t_info = t_infos[i]; + t_info.n = create_normal(t, its.vertices); + } + }); // END parallel for + // sum quadrics for (size_t i = 0; i < its.indices.size(); i++) { const Triangle &t = its.indices[i]; TriangleInfo &t_info = t_infos[i]; - t_info.n = create_normal(t, its.vertices); - SymMat q = create_quadric(t, t_info, its.vertices); + SymMat q = create_quadric(t, t_info.n, its.vertices); for (size_t e = 0; e < 3; e++) { VertexInfo &v_info = v_infos[t[e]]; v_info.q += q; @@ -361,12 +370,20 @@ std::tuple QuadricEdgeCollapse::init( v_info.count = 0; } assert(its.indices.size() * 3 == triangle_start); + + // calc error + tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), + [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const Triangle &t = its.indices[i]; + TriangleInfo & t_info = t_infos[i]; + t_info.e = calculate_error(t, its.vertices, v_infos); + } + }); // END parallel for - // calc error + create reference + // create reference for (size_t i = 0; i < its.indices.size(); i++) { - const Triangle &t = its.indices[i]; - TriangleInfo &t_info = t_infos[i]; - t_info.e = calculate_error(t, its.vertices, v_infos); + const Triangle &t = its.indices[i]; for (size_t j = 0; j < 3; ++j) { VertexInfo &v_info = v_infos[t[j]]; size_t ei = v_info.start + v_info.count; @@ -401,32 +418,7 @@ size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi, return -1; } - -bool QuadricEdgeCollapse::is_flipped(const Vec3f &vn, - const Vec3f &v1, - const Vec3f &v2, - const Vec3f &normal) -{ - static const float thr_pos = 1.0f - std::numeric_limits::epsilon(); - static const float thr_neg = -thr_pos; - static const float dot_thr = 0.2f; // Value from simplify mesh - - Vec3f d1 = v1 - vn; - d1.normalize(); - Vec3f d2 = v2 - vn; - d2.normalize(); - - float dot = d1.dot(d2); - if (dot > thr_pos || dot < thr_neg) return true; - - // IMPROVE: propagate new normal - Vec3f n = d1.cross(d2); - n.normalize(); - return n.dot(normal) < dot_thr; -} - - -bool QuadricEdgeCollapse::is_flipped(Vec3f & new_vertex, +bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, size_t ti0, size_t ti1, const VertexInfo & v_info, @@ -434,6 +426,10 @@ bool QuadricEdgeCollapse::is_flipped(Vec3f & new_vertex, const EdgeInfos & e_infos, const indexed_triangle_set &its) { + static const float thr_pos = 1.0f - std::numeric_limits::epsilon(); + static const float thr_neg = -thr_pos; + static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG + // for each vertex triangles size_t v_info_end = v_info.start + v_info.count; for (size_t ei = v_info.start; ei < v_info_end; ++ei) { @@ -445,8 +441,18 @@ bool QuadricEdgeCollapse::is_flipped(Vec3f & new_vertex, const Vec3f &normal = t_infos[e_info.t_index].n; const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]]; const Vec3f &vs = its.vertices[t[(e_info.edge + 2) % 3]]; - if (is_flipped(new_vertex, vf, vs, normal)) - return true; + + Vec3f d1 = vf - new_vertex; + d1.normalize(); + Vec3f d2 = vs - new_vertex; + d2.normalize(); + + float dot = d1.dot(d2); + if (dot > thr_pos || dot < thr_neg) return true; + // IMPROVE: propagate new normal + Vec3f n = d1.cross(d2); + n.normalize(); + if(n.dot(normal) < dot_thr) return true; } return false; } @@ -471,12 +477,12 @@ Error QuadricEdgeCollapse::calculate_error(const Triangle & t, } void QuadricEdgeCollapse::sub_quadric(const Triangle &t, - const TriangleInfo & t_info, + const Vec3f& normal, VertexInfos &v_infos, const Vertices &vertices) { - SymMat quadric = create_quadric(t, t_info, vertices); - for (size_t vi: t) v_infos[vi].q -= quadric; + SymMat quadric = create_quadric(t, normal, vertices); + for (auto vi: t) v_infos[vi].q -= quadric; } void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos, From f8d759ad6432c76f21c4daff57415cf244e11b71 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 23 Jun 2021 11:42:24 +0200 Subject: [PATCH 005/240] Assert check of new vertex can't be used general on All models(depends on scale) Fix variable name in compare function Remove second triangle from MPQ --- src/libslic3r/QuadricEdgeCollapse.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 7244b6c1aa..c59fa09b6f 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -124,9 +124,9 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, static constexpr float max_error = std::numeric_limits::max(); - auto cmp = [&t_infos](size_t vi0, size_t vi1) -> bool { - const Error &e0 = t_infos[vi0].e; - const Error &e1 = t_infos[vi1].e; + auto cmp = [&t_infos](size_t ti0, size_t ti1) -> bool { + const Error &e0 = t_infos[ti0].e; + const Error &e1 = t_infos[ti1].e; return e0.value < e1.value; }; // convert triangle index to priority queue index @@ -143,9 +143,9 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, mpq.pop(); TriangleInfo &t_info0 = t_infos[ti0]; if (t_info0.is_deleted()) continue; - Error &e = t_info0.e; - + if (e.value >= max_error) return false; // only flipped triangles + const Triangle &t0 = its.indices[ti0]; size_t vi0 = t0[e.min_index]; size_t vi1 = t0[(e.min_index+1) %3]; @@ -159,7 +159,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, SymMat q(v_info0.q); q += v_info1.q; Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); - assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1])); + //assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1])); // set of triangle indices that change quadric size_t ti1 = (v_info0.count < v_info1.count)? find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : @@ -170,6 +170,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, // IMPROVE1: what about other edges in triangle? // IMPROVE2: check mpq top if it is ti1 with same edge e.value = max_error; + // error is changed when surround edge is reduced mpq.push(ti0); continue; } @@ -185,8 +186,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, size_t v_info0_end = v_info0.start + v_info0.count; for (size_t di = v_info0.start; di < v_info0_end; ++di) { assert(di < e_infos.size()); - EdgeInfo &e_info = e_infos[di]; - size_t ti = e_info.t_index; + size_t ti = e_infos[di].t_index; if (ti == ti0) continue; // ti0 will be deleted if (ti == ti1) continue; // ti1 will be deleted sub_quadric(its.indices[ti], t_infos[ti].n, v_infos, its.vertices); @@ -228,8 +228,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, } // fix errors - must be after calculate all quadric - t_info1.e.value = max_error; // not neccessary when check deleted triangles at begining - //mpq.remove(i_convert[ti1]); + mpq.remove(i_convert[ti1]); for (size_t ti : changed_triangle_indices) { const Triangle &t = its.indices[ti]; t_infos[ti].e = calculate_error(t, its.vertices, v_infos); From 35a906139ebf900ada4ea5aeb1e59d7c8a7082f6 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Thu, 8 Jul 2021 08:33:06 +0200 Subject: [PATCH 006/240] Extend test with checking simplified model distance to original model --- tests/libslic3r/test_indexed_triangle_set.cpp | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index ac676267ac..094c17fd6c 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -103,6 +103,93 @@ TEST_CASE("Split two watertight meshes", "[its_split][its]") { } #include +static float triangle_area(const Vec3f &v0, const Vec3f &v1, const Vec3f &v2) +{ + Vec3f ab = v1 - v0; + Vec3f ac = v2 - v0; + return ab.cross(ac).norm() / 2.f; +} + +static float triangle_area(const Vec3crd &triangle_inices, const std::vector &vertices) +{ + return triangle_area(vertices[triangle_inices[0]], + vertices[triangle_inices[1]], + vertices[triangle_inices[2]]); +} + +static std::mt19937 create_random_generator() { + std::random_device rd; + std::mt19937 gen(rd()); + return gen; +} + +std::vector its_sample_surface(const indexed_triangle_set &its, + double sample_per_mm2, + std::mt19937 &random_generator = create_random_generator()) +{ + std::vector samples; + std::uniform_real_distribution rand01(0.f, 1.f); + for (const auto &triangle_indices : its.indices) { + float area = triangle_area(triangle_indices, its.vertices); + float countf; + float fractional = std::modf(area * sample_per_mm2, &countf); + int count = static_cast(countf); + + float generate = rand01(random_generator); + if (generate < fractional) ++count; + if (count == 0) continue; + + const Vec3f &v0 = its.vertices[triangle_indices[0]]; + const Vec3f &v1 = its.vertices[triangle_indices[1]]; + const Vec3f &v2 = its.vertices[triangle_indices[2]]; + for (int c = 0; c < count; c++) { + // barycentric coordinate + Vec3f b; + b[0] = rand01(random_generator); + b[1] = rand01(random_generator); + if ((b[0] + b[1]) > 1.f) { + b[0] = 1.f - b[0]; + b[1] = 1.f - b[1]; + } + b[2] = 1.f - b[0] - b[1]; + Vec3f pos; + for (int i = 0; i < 3; i++) { + pos[i] = b[0] * v0[i] + b[1] * v1[i] + b[2] * v2[i]; + } + samples.push_back(pos); + } + } + return samples; +} + + +#include "libslic3r/AABBTreeIndirect.hpp" + +// return Average abs distance to original +float compare(const indexed_triangle_set &original, + const indexed_triangle_set &simplified, + double sample_per_mm2) +{ + // create ABBTree + auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + original.vertices, original.indices); + + unsigned int init = 0; + std::mt19937 rnd(init); + auto samples = its_sample_surface(simplified, sample_per_mm2, rnd); + + float sumDistance = 0; + for (const Vec3f &sample : samples) { + size_t hit_idx; + Vec3f hit_point; + float distance2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + original.vertices, original.indices, tree, sample, hit_idx, + hit_point); + sumDistance += sqrt(distance2); + } + return sumDistance / samples.size(); +} + TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") { indexed_triangle_set its; @@ -139,6 +226,8 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") (v[i] > v4[i] && v[i] < v2[i]); CHECK(is_between); } + float avg_distance = compare(its_, its, 10); + CHECK(avg_distance < 8e-3f); } #include "test_utils.hpp" @@ -154,4 +243,7 @@ TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); CHECK(fabs(original_volume - volume) < 30.); + + float avg_distance = compare(mesh.its, its, 10); + CHECK(avg_distance < 0.021f); // 0.02022 | 0.0199614074 } \ No newline at end of file From c00dca7810e7eec388af1c76525cea17d9a76d30 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Thu, 8 Jul 2021 16:17:36 +0200 Subject: [PATCH 007/240] Speed up QEC by move error directly into MutablePriorityQueue Memory optimization: Change size_t to 32bit int Lightweight Error structure for faster PriorityQueue --- src/libslic3r/QuadricEdgeCollapse.cpp | 343 +++++++++++++------------- src/libslic3r/QuadricEdgeCollapse.hpp | 12 +- 2 files changed, 179 insertions(+), 176 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index c59fa09b6f..da409ff58d 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -12,73 +12,83 @@ namespace QuadricEdgeCollapse { using Indices = std::vector; using SymMat = SimplifyMesh::implementation::SymetricMatrix; + // smallest error caused by edges, identify smallest edge in triangle struct Error { - float value; + float value = -1.; // range(0 .. 2), - unsigned char min_index; - Error(float value, unsigned char min_index): value(value), min_index(min_index) { + uint32_t triangle_index = 0; + Error(float value, uint32_t triangle_index) + : value(value) + , triangle_index(triangle_index) + { assert(min_index < 3); } Error() = default; }; using Errors = std::vector; + // merge information together - faster access during processing - struct TriangleInfo - { + struct TriangleInfo { Vec3f n; // normalized normal - speed up calcualtion of q and check flip - Error e; // smallest error caused by edges, identify smallest edge in triangle + unsigned char min_index = 0; // identify edge for minimal Error -> lightweight Error structure TriangleInfo() = default; - bool is_deleted() const { return e.min_index > 2; } - void set_deleted() { e.min_index = 3; } + bool is_deleted() const { return n.x() > 2.f; } + void set_deleted() { n.x() = 3.f; } }; using TriangleInfos = std::vector; - struct VertexInfo - { + struct VertexInfo { SymMat q; // sum quadric of surround triangles - size_t start = 0, count = 0; // vertex neighbor triangles + uint32_t start = 0, count = 0; // vertex neighbor triangles VertexInfo() = default; bool is_deleted() const { return count == 0; } }; using VertexInfos = std::vector; - struct EdgeInfo - { - size_t t_index=0; // triangle index + struct EdgeInfo { + uint32_t t_index=0; // triangle index unsigned char edge = 0; // 0 or 1 or 2 EdgeInfo() = default; }; using EdgeInfos = std::vector; + // DTO for change neighbors + struct CopyEdgeInfo { + uint32_t start; + uint32_t count; + uint32_t move; + CopyEdgeInfo(uint32_t start, uint32_t count, uint32_t move) + : start(start), count(count), move(move) + {} + }; + using CopyEdgeInfos = std::vector; + Vec3f create_normal(const Triangle &triangle, const Vertices &vertices); - double calculate_error(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices); - Vec3f calculate_vertex(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices); + double calculate_error(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices); + Vec3f calculate_vertex(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices); // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number double vertex_error(const SymMat &q, const Vec3d &vertex); SymMat create_quadric(const Triangle &t, const Vec3f& normal, const Vertices &vertices); - std::tuple init(const indexed_triangle_set &its); - size_t find_triangle_index1(size_t vi, const VertexInfo& v_info, size_t ti, const EdgeInfos& e_infos, const Indices& indices); - bool is_flipped(const Vec3f &new_vertex, size_t ti0, size_t ti1, const VertexInfo& v_info, + std::tuple init(const indexed_triangle_set &its); + uint32_t find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); + bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); - // find edge with smallest error in triangle - Error calculate_error(const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos); - // subtract quadric of one triangle from triangle vertex - void sub_quadric(const Triangle &t, const Vec3f& normal, VertexInfos &v_infos, const Vertices &vertices); - - void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, size_t ti); - void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, size_t ti0, size_t ti1, - size_t vi0, size_t vi1, size_t vi_top0, const Triangle &t1); - + Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index); + void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, uint32_t ti); + void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, uint32_t ti0, uint32_t ti1, + uint32_t vi0, uint32_t vi1, uint32_t vi_top0, + const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1); void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its); } using namespace QuadricEdgeCollapse; +#ifdef NDEBUG bool check_neighbors(TriangleInfos &t_infos, Indices& indices, VertexInfos & v_infos) { - std::vector t_counts(v_infos.size(), 0); + std::vector t_counts(v_infos.size(), 0); for (size_t i = 0; i < indices.size(); i++) { TriangleInfo &t_info = t_infos[i]; if (t_info.is_deleted()) continue; @@ -114,41 +124,53 @@ bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { return true; } -bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, - size_t triangle_count) +#endif // NDEBUG + +void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, + uint32_t triangle_count, + float * max_error) { - TriangleInfos t_infos; + TriangleInfos t_infos; // only normals with information about deleted triangle VertexInfos v_infos; EdgeInfos e_infos; - std::tie(t_infos, v_infos, e_infos) = init(its); + Errors errors; + std::tie(t_infos, v_infos, e_infos, errors) = init(its); - static constexpr float max_error = std::numeric_limits::max(); - - auto cmp = [&t_infos](size_t ti0, size_t ti1) -> bool { - const Error &e0 = t_infos[ti0].e; - const Error &e1 = t_infos[ti1].e; - return e0.value < e1.value; - }; - // convert triangle index to priority queue index - std::vector i_convert(its.indices.size(), {0}); - auto setter = [&i_convert](size_t it, size_t index) { i_convert[it] = index; }; - MutablePriorityQueue mpq(std::move(setter), std::move(cmp)); + float max_float = std::numeric_limits::max(); + float last_collapsed_error = 0.f; + if (max_error == nullptr) { + max_error = &max_float; + } + + // convert from triangle index to mutable priority queue index + std::vector ti_2_mpqi(its.indices.size(), {0}); + auto setter = [&ti_2_mpqi](const Error &e, size_t index) { ti_2_mpqi[e.triangle_index] = index; }; + auto less = [](const Error &e1, const Error &e2) -> bool { return e1.value < e2.value; }; + MutablePriorityQueue mpq(std::move(setter), std::move(less)); mpq.reserve(its.indices.size()); - for (size_t i = 0; i < its.indices.size(); i++) mpq.push(i); + for (Error &error :errors) mpq.push(error); - size_t actual_triangle_count = its.indices.size(); + const size_t max_triangle_count_for_one_vertex = 50; + CopyEdgeInfos ceis; + ceis.reserve(max_triangle_count_for_one_vertex); + EdgeInfos e_infos_swap; + e_infos_swap.reserve(max_triangle_count_for_one_vertex); + std::vector changed_triangle_indices; + changed_triangle_indices.reserve(2 * max_triangle_count_for_one_vertex); + + uint32_t actual_triangle_count = its.indices.size(); while (actual_triangle_count > triangle_count && !mpq.empty()) { // triangle index 0 - size_t ti0 = mpq.top(); + Error e = mpq.top(); // copy + if (e.value >= *max_error) break; // Too big error mpq.pop(); + uint32_t ti0 = e.triangle_index; TriangleInfo &t_info0 = t_infos[ti0]; if (t_info0.is_deleted()) continue; - Error &e = t_info0.e; - if (e.value >= max_error) return false; // only flipped triangles const Triangle &t0 = its.indices[ti0]; - size_t vi0 = t0[e.min_index]; - size_t vi1 = t0[(e.min_index+1) %3]; + uint32_t vi0 = t0[t_info0.min_index]; + uint32_t vi1 = t0[(t_info0.min_index+1) %3]; // Need by move of neighbor edge infos in function: change_neighbors if (vi0 > vi1) std::swap(vi0, vi1); VertexInfo &v_info0 = v_infos[vi0]; @@ -161,7 +183,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); //assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1])); // set of triangle indices that change quadric - size_t ti1 = (v_info0.count < v_info1.count)? + uint32_t ti1 = (v_info0.count < v_info1.count)? find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; @@ -169,73 +191,63 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) { // IMPROVE1: what about other edges in triangle? // IMPROVE2: check mpq top if it is ti1 with same edge - e.value = max_error; + e.value = std::numeric_limits::max(); // error is changed when surround edge is reduced - mpq.push(ti0); + mpq.push(e); continue; } - - std::vector changed_triangle_indices; + last_collapsed_error = e.value; + changed_triangle_indices.clear(); changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4); - - sub_quadric(t0, t_info0.n, v_infos, its.vertices); - TriangleInfo &t_info1 = t_infos[ti1]; - const Triangle &t1 = its.indices[ti1]; - sub_quadric(t1, t_info1.n, v_infos, its.vertices); + // for each vertex0 triangles - size_t v_info0_end = v_info0.start + v_info0.count; - for (size_t di = v_info0.start; di < v_info0_end; ++di) { + uint32_t v_info0_end = v_info0.start + v_info0.count; + for (uint32_t di = v_info0.start; di < v_info0_end; ++di) { assert(di < e_infos.size()); - size_t ti = e_infos[di].t_index; + uint32_t ti = e_infos[di].t_index; if (ti == ti0) continue; // ti0 will be deleted if (ti == ti1) continue; // ti1 will be deleted - sub_quadric(its.indices[ti], t_infos[ti].n, v_infos, its.vertices); changed_triangle_indices.emplace_back(ti); } // for each vertex1 triangles - size_t v_info1_end = v_info1.start + v_info1.count; - for (size_t di = v_info1.start; di < v_info1_end; ++di) { + uint32_t v_info1_end = v_info1.start + v_info1.count; + for (uint32_t di = v_info1.start; di < v_info1_end; ++di) { assert(di < e_infos.size()); EdgeInfo &e_info = e_infos[di]; - size_t ti = e_info.t_index; + uint32_t ti = e_info.t_index; if (ti == ti0) continue; // ti0 will be deleted if (ti == ti1) continue; // ti1 will be deleted Triangle &t = its.indices[ti]; - sub_quadric(t, t_infos[ti].n, v_infos, its.vertices); t[e_info.edge] = vi0; // change index changed_triangle_indices.emplace_back(ti); } + v_info0.q = q; // fix neighbors // vertex index of triangle 0 which is not vi0 nor vi1 - size_t vi_top0 = t0[(e.min_index + 2) % 3]; - change_neighbors(e_infos, v_infos, ti0, ti1, vi0, vi1, vi_top0, t1); + uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3]; + const Triangle &t1 = its.indices[ti1]; + change_neighbors(e_infos, v_infos, ti0, ti1, vi0, vi1, + vi_top0, t1, ceis, e_infos_swap); // Change vertex // Has to be set after subtract quadric its.vertices[vi0] = new_vertex0; - // add new quadrics - v_info0.q = SymMat(); // zero value - for (size_t ti : changed_triangle_indices) { - const Triangle& t = its.indices[ti]; - TriangleInfo &t_info = t_infos[ti]; - t_info.n = create_normal(t, its.vertices); // new normal - SymMat q = create_quadric(t, t_info.n, its.vertices); - for (const size_t vi: t) v_infos[vi].q += q; - } - - // fix errors - must be after calculate all quadric - mpq.remove(i_convert[ti1]); - for (size_t ti : changed_triangle_indices) { - const Triangle &t = its.indices[ti]; - t_infos[ti].e = calculate_error(t, its.vertices, v_infos); - mpq.update(i_convert[ti]); + // fix errors - must be after set neighbors - v_infos + mpq.remove(ti_2_mpqi[ti1]); + for (uint32_t ti : changed_triangle_indices) { + size_t priority_queue_index = ti_2_mpqi[ti]; + auto iterator = mpq.begin() + priority_queue_index; + TriangleInfo& t_info = t_infos[ti]; + *iterator = calculate_error(ti, its.indices[ti], its.vertices, v_infos, t_info.min_index); + mpq.update(priority_queue_index); } // set triangle(0 + 1) indices as deleted + TriangleInfo &t_info1 = t_infos[ti1]; t_info0.set_deleted(); t_info1.set_deleted(); // triangle counter decrementation @@ -246,10 +258,10 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, // compact triangle compact(v_infos, t_infos, e_infos, its); - return true; + *max_error = last_collapsed_error; } -Vec3f QuadricEdgeCollapse::create_normal(const Triangle & triangle, +Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle, const Vertices &vertices) { const Vec3f &v0 = vertices[triangle[0]]; @@ -261,9 +273,9 @@ Vec3f QuadricEdgeCollapse::create_normal(const Triangle & triangle, return n; } -double QuadricEdgeCollapse::calculate_error(size_t id_v1, - size_t id_v2, - SymMat & q, +double QuadricEdgeCollapse::calculate_error(uint32_t id_v1, + uint32_t id_v2, + SymMat & q, const Vertices &vertices) { double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); @@ -288,8 +300,8 @@ double QuadricEdgeCollapse::calculate_error(size_t id_v1, } // similar as calculate error but focus on new vertex without calculation of error -Vec3f QuadricEdgeCollapse::calculate_vertex(size_t id_v1, - size_t id_v2, +Vec3f QuadricEdgeCollapse::calculate_vertex(uint32_t id_v1, + uint32_t id_v2, SymMat & q, const Vertices &vertices) { @@ -331,13 +343,13 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle & t, return SymMat(n.x(), n.y(), n.z(), -n.dot(v0)); } -std::tuple QuadricEdgeCollapse::init( - const indexed_triangle_set &its) +std::tuple +QuadricEdgeCollapse::init(const indexed_triangle_set &its) { TriangleInfos t_infos(its.indices.size()); VertexInfos v_infos(its.vertices.size()); EdgeInfos e_infos(its.indices.size() * 3); - + Errors errors(its.indices.size()); // calculate normals tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), [&](const tbb::blocked_range &range) { @@ -361,7 +373,7 @@ std::tuple QuadricEdgeCollapse::init( } // set offseted starts - size_t triangle_start = 0; + uint32_t triangle_start = 0; for (VertexInfo &v_info : v_infos) { v_info.start = triangle_start; triangle_start += v_info.count; @@ -376,7 +388,7 @@ std::tuple QuadricEdgeCollapse::init( for (size_t i = range.begin(); i < range.end(); ++i) { const Triangle &t = its.indices[i]; TriangleInfo & t_info = t_infos[i]; - t_info.e = calculate_error(t, its.vertices, v_infos); + errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index); } }); // END parallel for @@ -393,18 +405,18 @@ std::tuple QuadricEdgeCollapse::init( ++v_info.count; } } - return {t_infos, v_infos, e_infos}; + return {t_infos, v_infos, e_infos, errors}; } -size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi, - const VertexInfo &v_info, - size_t ti0, - const EdgeInfos & e_infos, - const Indices &indices) +uint32_t QuadricEdgeCollapse::find_triangle_index1(uint32_t vi, + const VertexInfo &v_info, + uint32_t ti0, + const EdgeInfos & e_infos, + const Indices & indices) { coord_t vi_coord = static_cast(vi); - size_t end = v_info.start + v_info.count; - for (size_t ei = v_info.start; ei < end; ++ei) { + uint32_t end = v_info.start + v_info.count; + for (uint32_t ei = v_info.start; ei < end; ++ei) { const EdgeInfo &e_info = e_infos[ei]; if (e_info.t_index == ti0) continue; const Triangle& t = indices[e_info.t_index]; @@ -417,12 +429,12 @@ size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi, return -1; } -bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, - size_t ti0, - size_t ti1, - const VertexInfo & v_info, - const TriangleInfos &t_infos, - const EdgeInfos & e_infos, +bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, + uint32_t ti0, + uint32_t ti1, + const VertexInfo & v_info, + const TriangleInfos & t_infos, + const EdgeInfos & e_infos, const indexed_triangle_set &its) { static const float thr_pos = 1.0f - std::numeric_limits::epsilon(); @@ -456,37 +468,29 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, return false; } -Error QuadricEdgeCollapse::calculate_error(const Triangle & t, +Error QuadricEdgeCollapse::calculate_error(uint32_t ti, + const Triangle & t, const Vertices & vertices, - const VertexInfos &v_infos) + const VertexInfos &v_infos, + unsigned char & min_index) { Vec3d error; for (size_t j = 0; j < 3; ++j) { - size_t j2 = (j == 2) ? 0 : (j + 1); - size_t vi0 = t[j]; - size_t vi1 = t[j2]; + size_t j2 = (j == 2) ? 0 : (j + 1); + uint32_t vi0 = t[j]; + uint32_t vi1 = t[j2]; SymMat q(v_infos[vi0].q); // copy q += v_infos[vi1].q; error[j] = calculate_error(vi0, vi1, q, vertices); } - unsigned char min_index = (error[0] < error[1]) ? - ((error[0] < error[2]) ? 0 : 2) : - ((error[1] < error[2]) ? 1 : 2); - return Error(static_cast(error[min_index]), min_index); -} - -void QuadricEdgeCollapse::sub_quadric(const Triangle &t, - const Vec3f& normal, - VertexInfos &v_infos, - const Vertices &vertices) -{ - SymMat quadric = create_quadric(t, normal, vertices); - for (auto vi: t) v_infos[vi].q -= quadric; + min_index = (error[0] < error[1]) ? ((error[0] < error[2]) ? 0 : 2) : + ((error[1] < error[2]) ? 1 : 2); + return Error(static_cast(error[min_index]), ti); } void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos, VertexInfo &v_info, - size_t ti) + uint32_t ti) { auto e_info = e_infos.begin() + v_info.start; auto e_info_end = e_info + v_info.count - 1; @@ -504,19 +508,20 @@ void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos, void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, VertexInfos & v_infos, - size_t ti0, - size_t ti1, - size_t vi0, - size_t vi1, - size_t vi_top0, - const Triangle &t1) + uint32_t ti0, + uint32_t ti1, + uint32_t vi0, + uint32_t vi1, + uint32_t vi_top0, + const Triangle &t1, + CopyEdgeInfos& infos, + EdgeInfos & e_infos1) { // have to copy Edge info from higher vertex index into smaller assert(vi0 < vi1); - // vertex index of triangle 1 which is not vi0 nor vi1 - size_t vi_top1 = t1[0]; + uint32_t vi_top1 = t1[0]; if (vi_top1 == vi0 || vi_top1 == vi1) { vi_top1 = t1[1]; if (vi_top1 == vi0 || vi_top1 == vi1) vi_top1 = t1[2]; @@ -528,15 +533,15 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, VertexInfo &v_info0 = v_infos[vi0]; VertexInfo &v_info1 = v_infos[vi1]; - size_t new_triangle_count = v_info0.count + v_info1.count - 4; + uint32_t new_triangle_count = v_info0.count + v_info1.count - 4; remove_triangle(e_infos, v_info0, ti0); remove_triangle(e_infos, v_info0, ti1); // copy second's edge infos out of e_infos, to free size - EdgeInfos e_infos1; + e_infos1.clear(); e_infos1.reserve(v_info1.count - 2); - size_t v_info_s_end = v_info1.start + v_info1.count; - for (size_t ei = v_info1.start; ei < v_info_s_end; ++ei) { + uint32_t v_info_s_end = v_info1.start + v_info1.count; + for (uint32_t ei = v_info1.start; ei < v_info_s_end; ++ei) { const EdgeInfo &e_info = e_infos[ei]; if (e_info.t_index == ti0) continue; if (e_info.t_index == ti1) continue; @@ -544,33 +549,25 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, } v_info1.count = 0; - size_t need = (new_triangle_count < v_info0.count)? 0: + uint32_t need = (new_triangle_count < v_info0.count)? 0: (new_triangle_count - v_info0.count); - size_t act_vi = vi0 + 1; + uint32_t act_vi = vi0 + 1; VertexInfo *act_v_info = &v_infos[act_vi]; - size_t act_start = act_v_info->start; - size_t last_end = v_info0.start + v_info0.count; + uint32_t act_start = act_v_info->start; + uint32_t last_end = v_info0.start + v_info0.count; + + infos.clear(); + infos.reserve(need); - struct CopyEdgeInfo - { - size_t start; - size_t count; - unsigned char move; - CopyEdgeInfo(size_t start, size_t count, unsigned char move) - : start(start), count(count), move(move) - {} - }; - std::vector c_infos; - c_infos.reserve(need); while (true) { - size_t save = act_start - last_end; + uint32_t save = act_start - last_end; if (save > 0) { if (save >= need) break; need -= save; - c_infos.emplace_back(act_v_info->start, act_v_info->count, need); + infos.emplace_back(act_v_info->start, act_v_info->count, need); } else { - c_infos.back().count += act_v_info->count; + infos.back().count += act_v_info->count; } last_end = act_v_info->start + act_v_info->count; act_v_info->start += need; @@ -583,15 +580,15 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos, } // copy by c_infos - for (size_t i = c_infos.size(); i > 0; --i) { - const CopyEdgeInfo &c_info = c_infos[i - 1]; - for (size_t ei = c_info.start + c_info.count - 1; ei >= c_info.start; --ei) + for (uint32_t i = infos.size(); i > 0; --i) { + const CopyEdgeInfo &c_info = infos[i - 1]; + for (uint32_t ei = c_info.start + c_info.count - 1; ei >= c_info.start; --ei) e_infos[ei + c_info.move] = e_infos[ei]; // copy } // copy triangle from first info into second - for (size_t ei_s = 0; ei_s < e_infos1.size(); ++ei_s) { - size_t ei_f = v_info0.start + v_info0.count; + for (uint32_t ei_s = 0; ei_s < e_infos1.size(); ++ei_s) { + uint32_t ei_f = v_info0.start + v_info0.count; e_infos[ei_f] = e_infos1[ei_s]; // copy ++v_info0.count; } @@ -602,12 +599,12 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, const EdgeInfos & e_infos, indexed_triangle_set &its) { - size_t vi_new = 0; - for (size_t vi = 0; vi < v_infos.size(); vi++) { + uint32_t vi_new = 0; + for (uint32_t vi = 0; vi < v_infos.size(); ++vi) { const VertexInfo &v_info = v_infos[vi]; if (v_info.is_deleted()) continue; // deleted - size_t e_info_end = v_info.start + v_info.count; - for (size_t ei = v_info.start; ei < e_info_end; ei++) { + uint32_t e_info_end = v_info.start + v_info.count; + for (uint32_t ei = v_info.start; ei < e_info_end; ++ei) { const EdgeInfo &e_info = e_infos[ei]; // change vertex index its.indices[e_info.t_index][e_info.edge] = vi_new; @@ -618,8 +615,8 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, // remove vertices tail its.vertices.erase(its.vertices.begin() + vi_new, its.vertices.end()); - size_t ti_new = 0; - for (size_t ti = 0; ti < t_infos.size(); ti++) { + uint32_t ti_new = 0; + for (uint32_t ti = 0; ti < t_infos.size(); ti++) { const TriangleInfo &t_info = t_infos[ti]; if (t_info.is_deleted()) continue; its.indices[ti_new++] = its.indices[ti]; diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp index d435656b20..422227d288 100644 --- a/src/libslic3r/QuadricEdgeCollapse.hpp +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -2,6 +2,7 @@ // sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/ // inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification +#include #include "TriangleMesh.hpp" namespace Slic3r { @@ -10,8 +11,13 @@ namespace Slic3r { /// Simplify mesh by Quadric metric /// /// IN/OUT triangle mesh to be simplified. -/// wanted triangle count. -/// TRUE on success otherwise FALSE -bool its_quadric_edge_collapse(indexed_triangle_set &its, size_t triangle_count); +/// Wanted triangle count. +/// Maximal Quadric for reduce. +/// When nullptr then max float is used +/// Output: Last used ErrorValue to collapse edge +/// +void its_quadric_edge_collapse(indexed_triangle_set &its, + uint32_t triangle_count = 0, + float * max_error = nullptr); } // namespace Slic3r From e26bffadd8fbbef29431ab7696dfed8f3dec82cc Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 9 Jul 2021 09:09:52 +0200 Subject: [PATCH 008/240] Add throw_on_cancel and statusfn into QEC --- src/libslic3r/QuadricEdgeCollapse.cpp | 46 ++++++++++++++----- src/libslic3r/QuadricEdgeCollapse.hpp | 15 ++++-- tests/libslic3r/test_indexed_triangle_set.cpp | 13 +++--- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index da409ff58d..e17fbe9c85 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -126,21 +126,29 @@ bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { #endif // NDEBUG -void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, - uint32_t triangle_count, - float * max_error) +void Slic3r::its_quadric_edge_collapse( + indexed_triangle_set & its, + uint32_t triangle_count, + float * max_error, + std::function throw_on_cancel, + std::function statusfn) { + // constants --> may be move to config + const int status_init_size = 10; // in percents + const int check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel + + // check input + if (triangle_count >= its.indices.size()) return; + float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error; + if (maximal_error <= 0.f) return; + TriangleInfos t_infos; // only normals with information about deleted triangle VertexInfos v_infos; EdgeInfos e_infos; Errors errors; std::tie(t_infos, v_infos, e_infos, errors) = init(its); - - float max_float = std::numeric_limits::max(); - float last_collapsed_error = 0.f; - if (max_error == nullptr) { - max_error = &max_float; - } + throw_on_cancel(); + statusfn(status_init_size); // convert from triangle index to mutable priority queue index std::vector ti_2_mpqi(its.indices.size(), {0}); @@ -159,10 +167,26 @@ void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, changed_triangle_indices.reserve(2 * max_triangle_count_for_one_vertex); uint32_t actual_triangle_count = its.indices.size(); + uint32_t count_triangle_to_reduce = actual_triangle_count - triangle_count; + auto increase_status = [&]() { + double reduced = (actual_triangle_count - triangle_count) / + (double) count_triangle_to_reduce; + double status = (100 - status_init_size) * (1. - reduced); + statusfn(static_cast(std::round(status))); + }; + // modulo for update status + uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100); + + uint32_t iteration_number = 0; + float last_collapsed_error = 0.f; while (actual_triangle_count > triangle_count && !mpq.empty()) { + ++iteration_number; + if (iteration_number % status_mod == 0) increase_status(); + if (iteration_number % check_cancel_period == 0) throw_on_cancel(); + // triangle index 0 Error e = mpq.top(); // copy - if (e.value >= *max_error) break; // Too big error + if (e.value >= maximal_error) break; // Too big error mpq.pop(); uint32_t ti0 = e.triangle_index; TriangleInfo &t_info0 = t_infos[ti0]; @@ -258,7 +282,7 @@ void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, // compact triangle compact(v_infos, t_infos, e_infos, its); - *max_error = last_collapsed_error; + if (max_error != nullptr) *max_error = last_collapsed_error; } Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle, diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp index 422227d288..3fcf61a08c 100644 --- a/src/libslic3r/QuadricEdgeCollapse.hpp +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -3,6 +3,7 @@ // inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification #include +#include #include "TriangleMesh.hpp" namespace Slic3r { @@ -14,10 +15,14 @@ namespace Slic3r { /// Wanted triangle count. /// Maximal Quadric for reduce. /// When nullptr then max float is used -/// Output: Last used ErrorValue to collapse edge -/// -void its_quadric_edge_collapse(indexed_triangle_set &its, - uint32_t triangle_count = 0, - float * max_error = nullptr); +/// Output: Last used ErrorValue to collapse edge +/// Could stop process of calculation. +/// Give a feed back to user about progress. +void its_quadric_edge_collapse( + indexed_triangle_set & its, + uint32_t triangle_count = 0, + float * max_error = nullptr, + std::function throw_on_cancel = nullptr, + std::function statusfn = nullptr); } // namespace Slic3r diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 094c17fd6c..087636edf5 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -167,8 +167,8 @@ std::vector its_sample_surface(const indexed_triangle_set &its, // return Average abs distance to original float compare(const indexed_triangle_set &original, - const indexed_triangle_set &simplified, - double sample_per_mm2) + const indexed_triangle_set &simplified, + double sample_per_mm2) { // create ABBTree auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( @@ -203,8 +203,8 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") indexed_triangle_set its_ = its; // copy // its_write_obj(its, "tetrhedron_in.obj"); - size_t wanted_count = its.indices.size() - 1; - CHECK(its_quadric_edge_collapse(its, wanted_count)); + uint32_t wanted_count = its.indices.size() - 1; + its_quadric_edge_collapse(its, wanted_count); // its_write_obj(its, "tetrhedron_out.obj"); CHECK(its.indices.size() == 4); CHECK(its.vertices.size() == 4); @@ -235,11 +235,12 @@ TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") { TriangleMesh mesh = load_model("frog_legs.obj"); double original_volume = its_volume(mesh.its); - size_t wanted_count = mesh.its.indices.size() * 0.05; + uint32_t wanted_count = mesh.its.indices.size() * 0.05; REQUIRE_FALSE(mesh.empty()); indexed_triangle_set its = mesh.its; // copy - its_quadric_edge_collapse(its, wanted_count); // its_write_obj(its, "frog_legs_qec.obj"); + float max_error = std::numeric_limits::max(); + its_quadric_edge_collapse(its, wanted_count, &max_error); CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); CHECK(fabs(original_volume - volume) < 30.); From 6f9f4f78b79f09bf2f3e4c2b9182b03392f07f00 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 9 Jul 2021 09:10:03 +0200 Subject: [PATCH 009/240] Added SimplificationDialog --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_Factories.cpp | 11 +++ src/slic3r/GUI/GUI_Factories.hpp | 1 + src/slic3r/GUI/GUI_ObjectList.cpp | 12 ++++ src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/Plater.cpp | 36 ++++++++++ src/slic3r/GUI/Plater.hpp | 2 + src/slic3r/GUI/SimplificationDialog.cpp | 91 +++++++++++++++++++++++++ src/slic3r/GUI/SimplificationDialog.hpp | 25 +++++++ 9 files changed, 181 insertions(+) create mode 100644 src/slic3r/GUI/SimplificationDialog.cpp create mode 100644 src/slic3r/GUI/SimplificationDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 76fd8d9891..08dd66d9d1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES GUI/SavePresetDialog.cpp GUI/PhysicalPrinterDialog.hpp GUI/PhysicalPrinterDialog.cpp + GUI/SimplificationDialog.cpp + GUI/SimplificationDialog.hpp GUI/GUI_Factories.cpp GUI/GUI_Factories.hpp GUI/GUI_ObjectList.cpp diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index c4782615ce..d2d8602864 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -651,6 +651,15 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu) wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "", [](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu, []() {return plater()->can_fix_through_netfabb(); }, plater()); + + return menu_item; +} + +wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu) +{ + wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Symplify model"), "", + [](wxCommandEvent&) { obj_list()->simplify(); }, "", menu, + []() {return plater()->can_simplify(); }, plater()); menu->AppendSeparator(); return menu_item; @@ -868,6 +877,7 @@ void MenuFactory::create_common_object_menu(wxMenu* menu) append_menu_item_scale_selection_to_fit_print_volume(menu); append_menu_item_fix_through_netfabb(menu); + append_menu_item_simplify(menu); append_menu_items_mirror(menu); } @@ -917,6 +927,7 @@ void MenuFactory::create_part_menu() append_menu_item_replace_with_stl(menu); append_menu_item_export_stl(menu); append_menu_item_fix_through_netfabb(menu); + append_menu_item_simplify(menu); append_menu_items_mirror(menu); append_menu_item(menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual parts"), diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index e8928d3ffb..e0d091ed56 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -90,6 +90,7 @@ private: wxMenuItem* append_menu_item_printable(wxMenu* menu); void append_menu_items_osx(wxMenu* menu); wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); + wxMenuItem* append_menu_item_simplify(wxMenu* menu); void append_menu_item_export_stl(wxMenu* menu); void append_menu_item_reload_from_disk(wxMenu* menu); void append_menu_item_replace_with_stl(wxMenu* menu); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 443ef7815e..cea5dbcb92 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4,6 +4,7 @@ #include "GUI_Factories.hpp" #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" +#include "SimplificationDialog.hpp" #include "GUI_App.hpp" #include "I18N.hpp" #include "Plater.hpp" @@ -3759,6 +3760,17 @@ void ObjectList::fix_through_netfabb() update_item_error_icon(obj_idx, vol_idx); } +void ObjectList::simplify() +{ + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx); + + SimplificationDialog dlg(this); + dlg.ShowModal(); + + wxGetApp().plater()->simplify(obj_idx, vol_idx); +} + void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const { const wxDataViewItem item = vol_idx <0 ? m_objects_model->GetItemById(obj_idx) : diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 36b399e515..291518488a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -353,6 +353,7 @@ public: void split_instances(); void rename_item(); void fix_through_netfabb(); + void simplify(); void update_item_error_icon(const int obj_idx, int vol_idx) const ; void copy_layers_to_clipboard(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 49c0359bf7..756e74bae1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1730,6 +1730,7 @@ struct Plater::priv void replace_with_stl(); void reload_all_from_disk(); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); + void simplify(const int obj_idx, const int vol_idx = -1); void set_current_panel(wxPanel* panel); @@ -1782,6 +1783,7 @@ struct Plater::priv bool can_arrange() const; bool can_layers_editing() const; bool can_fix_through_netfabb() const; + bool can_simplify() const; bool can_set_instance_to_object() const; bool can_mirror() const; bool can_reload_from_disk() const; @@ -3593,6 +3595,32 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = this->schedule_background_process(); } +void Plater::priv::simplify(const int obj_idx, const int vol_idx/* = -1*/) +{ + if (obj_idx < 0) + return; + + // Do not fix anything when a gizmo is open. There might be issues with updates + // and what is worse, the snapshot time would refer to the internal stack. + if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) { + notification_manager->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Please close all manipulators available from " + "the left toolbar before fixing the mesh.")); + return; + } + + // size_t snapshot_time = undo_redo_stack().active_snapshot_time(); + Plater::TakeSnapshot snapshot(q, _L("Symplify model")); + + // ToDo + + this->update(); + this->object_list_changed(); + this->schedule_background_process(); +} + void Plater::priv::set_current_panel(wxPanel* panel) { if (std::find(panels.begin(), panels.end(), panel) == panels.end()) @@ -4375,6 +4403,12 @@ bool Plater::priv::can_fix_through_netfabb() const return model.objects[obj_idx]->get_mesh_errors_count() > 0; } + +bool Plater::priv::can_simplify() const +{ + return true; +} + bool Plater::priv::can_increase_instances() const { if (m_ui_jobs.is_any_running() @@ -6309,6 +6343,7 @@ void Plater::suppress_background_process(const bool stop_background_process) } void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); } +void Plater::simplify(const int obj_idx, const int vol_idx/* = -1*/) { p->simplify(obj_idx, vol_idx); } void Plater::mirror(Axis axis) { p->mirror(axis); } void Plater::split_object() { p->split_object(); } void Plater::split_volume() { p->split_volume(); } @@ -6509,6 +6544,7 @@ bool Plater::can_increase_instances() const { return p->can_increase_instances() bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); } bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); } bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); } +bool Plater::can_simplify() const { return p->can_simplify(); } bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); } bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); } bool Plater::can_arrange() const { return p->can_arrange(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index fc4001ba59..d58e6b9b12 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -236,6 +236,7 @@ public: bool is_background_process_update_scheduled() const; void suppress_background_process(const bool stop_background_process) ; void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); + void simplify(const int obj_idx, const int vol_idx = -1); void send_gcode(); void eject_drive(); @@ -314,6 +315,7 @@ public: bool can_decrease_instances() const; bool can_set_instance_to_object() const; bool can_fix_through_netfabb() const; + bool can_simplify() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; bool can_arrange() const; diff --git a/src/slic3r/GUI/SimplificationDialog.cpp b/src/slic3r/GUI/SimplificationDialog.cpp new file mode 100644 index 0000000000..780fc24075 --- /dev/null +++ b/src/slic3r/GUI/SimplificationDialog.cpp @@ -0,0 +1,91 @@ +#include "SimplificationDialog.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "format.hpp" +#include "wxExtensions.hpp" +#include "I18N.hpp" +#include "libslic3r/Utils.hpp" + + +namespace Slic3r { +namespace GUI { + +#define BORDER_W 10 + +SimplificationDialog::SimplificationDialog(wxWindow* parent) : + DPIDialog(parent, wxID_ANY, _L("Name of Dialog"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) +{ + SetFont(wxGetApp().normal_font()); + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Some text") + ":"); + + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 2, 1, 2); + grid_sizer->AddGrowableCol(1, 1); + grid_sizer->SetFlexibleDirection(wxBOTH); + + for (int i = 0; i < 3; i++) { + auto* text = new wxStaticText(this, wxID_ANY, _L("Text") + " " + std::to_string(i) + " :"); + +#ifdef _WIN32 + long style = wxBORDER_SIMPLE; +#else + long style = 0 +#endif + auto value = new wxTextCtrl(this, wxID_ANY, "Some Value", wxDefaultPosition, wxDefaultSize, style); + + grid_sizer->Add(text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); + grid_sizer->Add(value, 1, wxEXPAND | wxBOTTOM, 1); + } + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + this->Bind(wxEVT_BUTTON, &SimplificationDialog::OnOK, this, wxID_OK); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(grid_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + wxGetApp().UpdateDlgDarkUI(this); + + this->CenterOnScreen(); +} + +SimplificationDialog::~SimplificationDialog() +{ +} + +void SimplificationDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(45 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void SimplificationDialog::OnOK(wxEvent& event) +{ + event.Skip(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SimplificationDialog.hpp b/src/slic3r/GUI/SimplificationDialog.hpp new file mode 100644 index 0000000000..4c0210ee86 --- /dev/null +++ b/src/slic3r/GUI/SimplificationDialog.hpp @@ -0,0 +1,25 @@ +#ifndef slic3r_SimplificationDialog_hpp_ +#define slic3r_SimplificationDialog_hpp_ + +#include "GUI_Utils.hpp" + +namespace Slic3r { +namespace GUI { + +class SimplificationDialog : public DPIDialog +{ + void OnOK(wxEvent& event); + +public: + SimplificationDialog(wxWindow* parent); + ~SimplificationDialog(); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif //slic3r_SimplificationDialog_hpp_ From 2c0b89e4a7c6fb6bc89e0c9ee722b6c4778ae8e6 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 9 Jul 2021 10:31:05 +0200 Subject: [PATCH 010/240] FIX Simplify test --- src/libslic3r/QuadricEdgeCollapse.cpp | 10 +++++----- tests/libslic3r/test_indexed_triangle_set.cpp | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index e17fbe9c85..94375ec9d0 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -15,15 +15,12 @@ namespace QuadricEdgeCollapse { // smallest error caused by edges, identify smallest edge in triangle struct Error { - float value = -1.; - // range(0 .. 2), + float value = -1.; // identifying of smallest edge is stored inside of TriangleInfo uint32_t triangle_index = 0; Error(float value, uint32_t triangle_index) : value(value) , triangle_index(triangle_index) - { - assert(min_index < 3); - } + {} Error() = default; }; using Errors = std::vector; @@ -31,6 +28,7 @@ namespace QuadricEdgeCollapse { // merge information together - faster access during processing struct TriangleInfo { Vec3f n; // normalized normal - speed up calcualtion of q and check flip + // range(0 .. 2), unsigned char min_index = 0; // identify edge for minimal Error -> lightweight Error structure TriangleInfo() = default; bool is_deleted() const { return n.x() > 2.f; } @@ -141,6 +139,8 @@ void Slic3r::its_quadric_edge_collapse( if (triangle_count >= its.indices.size()) return; float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error; if (maximal_error <= 0.f) return; + if (throw_on_cancel == nullptr) throw_on_cancel = []() {}; + if (statusfn == nullptr) statusfn = [](int) {}; TriangleInfos t_infos; // only normals with information about deleted triangle VertexInfos v_infos; diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 087636edf5..e079cb3028 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -233,18 +233,17 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") #include "test_utils.hpp" TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") { - TriangleMesh mesh = load_model("frog_legs.obj"); + TriangleMesh mesh = load_model("frog_legs.obj"); double original_volume = its_volume(mesh.its); uint32_t wanted_count = mesh.its.indices.size() * 0.05; REQUIRE_FALSE(mesh.empty()); indexed_triangle_set its = mesh.its; // copy - // its_write_obj(its, "frog_legs_qec.obj"); float max_error = std::numeric_limits::max(); its_quadric_edge_collapse(its, wanted_count, &max_error); + //its_write_obj(its, "frog_legs_qec.obj"); CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); - CHECK(fabs(original_volume - volume) < 30.); - + CHECK(fabs(original_volume - volume) < 33.); float avg_distance = compare(mesh.its, its, 10); - CHECK(avg_distance < 0.021f); // 0.02022 | 0.0199614074 + CHECK(avg_distance < 0.022f); // 0.02022 | 0.0199614074 } \ No newline at end of file From 756d2694eb2035ce4bbbb86aa81f8e277ef09692 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 12 Jul 2021 16:36:08 +0200 Subject: [PATCH 011/240] FIX: Mutable priority queue --- src/libslic3r/MutablePriorityQueue.hpp | 2 +- src/libslic3r/QuadricEdgeCollapse.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/MutablePriorityQueue.hpp b/src/libslic3r/MutablePriorityQueue.hpp index 68cd01859c..cc1cae68c9 100644 --- a/src/libslic3r/MutablePriorityQueue.hpp +++ b/src/libslic3r/MutablePriorityQueue.hpp @@ -354,7 +354,7 @@ inline void MutableSkipHeapPriorityQueue::max()); + m_index_setter(m_heap[1], std::numeric_limits::max()); } // Zero'th element is padding, thus non-empty queue must have at least two elements. if (m_heap.size() > 2) { diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 94375ec9d0..c5de1b0a7f 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -151,10 +151,11 @@ void Slic3r::its_quadric_edge_collapse( statusfn(status_init_size); // convert from triangle index to mutable priority queue index - std::vector ti_2_mpqi(its.indices.size(), {0}); + std::vector ti_2_mpqi(its.indices.size(), {0}); auto setter = [&ti_2_mpqi](const Error &e, size_t index) { ti_2_mpqi[e.triangle_index] = index; }; auto less = [](const Error &e1, const Error &e2) -> bool { return e1.value < e2.value; }; - MutablePriorityQueue mpq(std::move(setter), std::move(less)); + auto mpq = make_miniheap_mutable_priority_queue(std::move(setter), std::move(less)); + //MutablePriorityQueue mpq(std::move(setter), std::move(less)); mpq.reserve(its.indices.size()); for (Error &error :errors) mpq.push(error); @@ -257,16 +258,14 @@ void Slic3r::its_quadric_edge_collapse( vi_top0, t1, ceis, e_infos_swap); // Change vertex - // Has to be set after subtract quadric its.vertices[vi0] = new_vertex0; // fix errors - must be after set neighbors - v_infos mpq.remove(ti_2_mpqi[ti1]); for (uint32_t ti : changed_triangle_indices) { size_t priority_queue_index = ti_2_mpqi[ti]; - auto iterator = mpq.begin() + priority_queue_index; TriangleInfo& t_info = t_infos[ti]; - *iterator = calculate_error(ti, its.indices[ti], its.vertices, v_infos, t_info.min_index); + mpq[priority_queue_index] = calculate_error(ti, its.indices[ti], its.vertices, v_infos, t_info.min_index); mpq.update(priority_queue_index); } From af526c54f4b421fb7dda636b86d2202b635ba80a Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 19 Jul 2021 09:17:50 +0200 Subject: [PATCH 012/240] Add simplification GUI --- src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/GUI_ObjectList.cpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 231 ++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 76 ++++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 13 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + src/slic3r/GUI/Plater.cpp | 111 ++++----- src/slic3r/GUI/Plater.hpp | 5 +- src/slic3r/GUI/SimplificationDialog.cpp | 91 ------- src/slic3r/GUI/SimplificationDialog.hpp | 25 -- tests/libslic3r/test_indexed_triangle_set.cpp | 3 +- .../libslic3r/test_mutable_priority_queue.cpp | 101 ++++++++ 12 files changed, 476 insertions(+), 195 deletions(-) create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp delete mode 100644 src/slic3r/GUI/SimplificationDialog.cpp delete mode 100644 src/slic3r/GUI/SimplificationDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 08dd66d9d1..d9e849deff 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -57,6 +57,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoPainterBase.hpp GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.hpp + GUI/Gizmos/GLGizmoSimplify.cpp + GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp GUI/GLSelectionRectangle.cpp @@ -97,8 +99,6 @@ set(SLIC3R_GUI_SOURCES GUI/SavePresetDialog.cpp GUI/PhysicalPrinterDialog.hpp GUI/PhysicalPrinterDialog.cpp - GUI/SimplificationDialog.cpp - GUI/SimplificationDialog.hpp GUI/GUI_Factories.cpp GUI/GUI_Factories.hpp GUI/GUI_ObjectList.cpp diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cea5dbcb92..8219550359 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4,7 +4,6 @@ #include "GUI_Factories.hpp" #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" -#include "SimplificationDialog.hpp" #include "GUI_App.hpp" #include "I18N.hpp" #include "Plater.hpp" @@ -3762,13 +3761,8 @@ void ObjectList::fix_through_netfabb() void ObjectList::simplify() { - int obj_idx, vol_idx; - get_selected_item_indexes(obj_idx, vol_idx); - - SimplificationDialog dlg(this); - dlg.ShowModal(); - - wxGetApp().plater()->simplify(obj_idx, vol_idx); + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); } void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp new file mode 100644 index 0000000000..ce11813479 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -0,0 +1,231 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoSimplify.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/QuadricEdgeCollapse.hpp" + +namespace Slic3r::GUI { + +GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, + const std::string &icon_filename, + unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, -1) + , state(State::settings) + , is_valid_result(false) + , progress(0) + , volume(nullptr) + , obj_index(0) + , need_reload(false) +{} + +GLGizmoSimplify::~GLGizmoSimplify() { + state = State::canceling; + if (worker.joinable()) worker.join(); +} + +bool GLGizmoSimplify::on_init() +{ + //m_grabbers.emplace_back(); + //m_shortcut_key = WXK_CONTROL_C; + return true; +} + +std::string GLGizmoSimplify::on_get_name() const +{ + return (_L("Simplify")).ToUTF8().data(); +} + +void GLGizmoSimplify::on_render() const{} +void GLGizmoSimplify::on_render_for_picking() const{} + +void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) +{ + const Selection &selection = m_parent.get_selection(); + int object_idx = selection.get_object_idx(); + ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; + ModelVolume *act_volume = obj->volumes.front(); + + + // Check selection of new volume + // Do not reselect object when processing + if (act_volume != volume && state == State::settings) { + obj_index = object_idx; // to remember correct object + volume = act_volume; + original_its = {}; + const TriangleMesh &tm = volume->mesh(); + c.wanted_percent = 50.; // default value + c.update_percent(tm.its.indices.size()); + is_valid_result = false; + } + + size_t triangle_count = volume->mesh().its.indices.size(); + // already reduced mesh + if (original_its.has_value()) + triangle_count = original_its->indices.size(); + + int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse; + m_imgui->begin(_L("Simplify mesh ") + volume->name, flag); + std::string description = "Reduce amout of triangle in mesh( " + + volume->name + " has " + + std::to_string(triangle_count) + " triangles)"; + ImGui::Text(description.c_str()); + // First initialization + fix triangle count + if (c.wanted_count > triangle_count) c.update_percent(triangle_count); + if (m_imgui->checkbox(_L("Until triangle count is less than "), c.use_count)) { + if (!c.use_count) c.use_error = true; + is_valid_result = false; + } + + m_imgui->disabled_begin(!c.use_count); + ImGui::SameLine(); + int wanted_count = c.wanted_count; + if (ImGui::SliderInt("triangles", &wanted_count, 0, + triangle_count, "%d")) { + c.wanted_count = static_cast(wanted_count); + c.update_count(triangle_count); + is_valid_result = false; + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(80); + int precision = (c.wanted_percent > 10)? 0: ((c.wanted_percent > 1)? 1:2); + float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); + if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { + if (c.wanted_percent < 0.f) c.wanted_percent = 0.f; + if (c.wanted_percent > 100.f) c.wanted_percent = 100.f; + c.update_percent(triangle_count); + is_valid_result = false; + } + m_imgui->disabled_end(); // use_count + + if (m_imgui->checkbox(_L("Until error"), c.use_error)) { + if (!c.use_error) c.use_count = true; + is_valid_result = false; + } + ImGui::SameLine(); + m_imgui->disabled_begin(!c.use_error); + if (ImGui::InputFloat("(maximal quadric error)", &c.max_error, 0.01f, .1f, 2)) { + if (c.max_error < 0.f) c.max_error = 0.f; + is_valid_result = false; + } + m_imgui->disabled_end(); // use_error + + if (state == State::settings) { + if (m_imgui->button(_L("Cancel"))) { + if (original_its.has_value()) { + set_its(*original_its); + state = State::close_on_end; + } else { + close(); + } + } + ImGui::SameLine(); + if (m_imgui->button(_L("Preview"))) { + state = State::simplifying; + // simplify but not aply on mesh + process(); + } + ImGui::SameLine(); + if (m_imgui->button(_L("Aply"))) { + if (!is_valid_result) { + state = State::close_on_end; + process(); + } else { + // use preview and close + close(); + } + } + } else { + m_imgui->disabled_begin(state == State::canceling); + if (m_imgui->button(_L("Cancel"))) state = State::canceling; + m_imgui->disabled_end(); + + ImGui::SameLine(); + // draw progress bar + ImGui::Text("Processing %c \t %d / 100", + "|/-\\"[(int) (ImGui::GetTime() / 0.05f) & 3], progress); + } + m_imgui->end(); + + if (need_reload) { + need_reload = false; + + // Reload visualization of mesh - change VBO, FBO on GPU + m_parent.reload_scene(true); // deactivate gizmo?? + GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); + gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); + + if (state == State::close_on_end) { + // fix hollowing, sla support points, modifiers, ... + auto plater = wxGetApp().plater(); + plater->changed_mesh(obj_index); // deactivate gizmo?? + // changed_mesh cause close(); + //close(); + } + + // change from simplifying | aply + state = State::settings; + + } +} + +void GLGizmoSimplify::close() { + volume = nullptr; + + // close gizmo == open it again + GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); + gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); +} + + +void GLGizmoSimplify::process() +{ + class SimplifyCanceledException : public std::exception { + public: + const char* what() const throw() { return L("Model simplification has been canceled"); } + }; + + if (!original_its.has_value()) + original_its = volume->mesh().its; // copy + + auto plater = wxGetApp().plater(); + plater->take_snapshot(_L("Simplify ") + volume->name); + plater->clear_before_change_mesh(obj_index); + progress = 0; + if (worker.joinable()) worker.join(); + worker = std::thread([&]() { + // store original triangles + uint32_t triangle_count = (c.use_count)? c.wanted_count : 0; + float* max_error = (c.use_error)? &c.max_error : nullptr; + std::function throw_on_cancel = [&]() { if ( state == State::canceling) throw SimplifyCanceledException(); }; + std::function statusfn = [&](int percent) { progress = percent; }; + indexed_triangle_set collapsed = original_its.value(); // copy + try { + its_quadric_edge_collapse(collapsed, triangle_count, max_error, throw_on_cancel, statusfn); + set_its(collapsed); + is_valid_result = true; + } catch (SimplifyCanceledException &) { + is_valid_result = false; + } + }); +} + +void GLGizmoSimplify::set_its(indexed_triangle_set &its) { + auto tm = std::make_unique(its); + tm->repair(); + volume->set_mesh(std::move(tm)); + volume->set_new_unique_id(); + volume->get_object()->invalidate_bounding_box(); + need_reload = true; +} + +bool GLGizmoSimplify::on_is_activable() const +{ + return !m_parent.get_selection().is_empty(); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp new file mode 100644 index 0000000000..299d559c77 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -0,0 +1,76 @@ +#ifndef slic3r_GLGizmoSimplify_hpp_ +#define slic3r_GLGizmoSimplify_hpp_ + +#include "GLGizmoBase.hpp" +#include "libslic3r/Model.hpp" +#include +#include + +namespace Slic3r { +namespace GUI { + +class GLGizmoSimplify : public GLGizmoBase +{ + enum class State { + settings, + simplifying, // start processing + canceling, // canceled + successfull, // successful simplified + close_on_end + } state; + + bool is_valid_result; // differ what to do in apply + + int progress; + + ModelVolume *volume; + size_t obj_index; + std::optional original_its; + + bool need_reload; // after simplify, glReload must be on main thread + std::thread worker; +public: + GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + virtual ~GLGizmoSimplify(); + void set_selectable(bool value); +protected: + virtual bool on_init() override; + virtual std::string on_get_name() const override; + virtual void on_render() const override; + virtual void on_render_for_picking() const override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; + virtual bool on_is_activable() const override; + virtual bool on_is_selectable() const override { return false; }; + +private: + void close(); + void process(); + void set_its(indexed_triangle_set &its); + struct Configuration + { + bool use_count = true; + // minimal triangle count + float wanted_percent = 50.f; + uint32_t wanted_count = 0; // initialize by percents + + bool use_error = false; + // maximal quadric error + float max_error = 1.; + + void update_count(size_t triangle_count) + { + wanted_percent = (float) wanted_count / triangle_count * 100.f; + } + void update_percent(size_t triangle_count) + { + wanted_count = static_cast( + std::round(triangle_count * wanted_percent / 100.f)); + } + }; + Configuration c; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoSimplify_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ace6c07857..577720218e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -20,6 +20,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -110,6 +111,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9)); + m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); @@ -169,7 +171,7 @@ void GLGizmosManager::reset_all_states() bool GLGizmosManager::open_gizmo(EType type) { int idx = int(type); - if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) { + if (/*m_gizmos[idx]->is_selectable() &&*/ m_gizmos[idx]->is_activable()) { activate_gizmo(m_current == idx ? Undefined : (EType)idx); update_data(); return true; @@ -1021,6 +1023,8 @@ void GLGizmosManager::do_render_overlay() const float u_offset = 1.0f / (float)tex_width; float v_offset = 1.0f / (float)tex_height; + float toolbar_top = 0.f; + float current_y = 0.f; for (size_t idx : selectable_idxs) { GLGizmoBase* gizmo = m_gizmos[idx].get(); @@ -1035,11 +1039,14 @@ void GLGizmosManager::do_render_overlay() const GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (idx == m_current) { - float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); - gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); + toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); + current_y = 0.5f * cnv_h - zoomed_top_y * zoom; } zoomed_top_y -= zoomed_stride_y; } + + if (m_current != Undefined) + m_gizmos[m_current]->render_input_window(width, current_y, toolbar_top); } float GLGizmosManager::get_scaled_total_height() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 01d7ea85c8..383d7099fa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -69,6 +69,7 @@ public: FdmSupports, Seam, MmuSegmentation, + Simplify, Undefined }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 756e74bae1..f2ef0b01b0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1730,7 +1730,6 @@ struct Plater::priv void replace_with_stl(); void reload_all_from_disk(); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); - void simplify(const int obj_idx, const int vol_idx = -1); void set_current_panel(wxPanel* panel); @@ -3555,70 +3554,10 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = // size_t snapshot_time = undo_redo_stack().active_snapshot_time(); Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb")); + q->clear_before_change_mesh(obj_idx); ModelObject* mo = model.objects[obj_idx]; - - // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh - // may be different and they would make no sense. - bool paint_removed = false; - for (ModelVolume* mv : mo->volumes) { - paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty(); - mv->supported_facets.clear(); - mv->seam_facets.clear(); - mv->mmu_segmentation_facets.clear(); - } - if (paint_removed) { - // snapshot_time is captured by copy so the lambda knows where to undo/redo to. - notification_manager->push_notification( - NotificationType::CustomSupportsAndSeamRemovedAfterRepair, - NotificationManager::NotificationLevel::RegularNotification, - _u8L("Custom supports and seams were removed after repairing the mesh.")); -// _u8L("Undo the repair"), -// [this, snapshot_time](wxEvtHandler*){ -// // Make sure the snapshot is still available and that -// // we are in the main stack and not in a gizmo-stack. -// if (undo_redo_stack().has_undo_snapshot(snapshot_time) -// && q->canvas3D()->get_gizmos_manager().get_current() == nullptr) -// undo_redo_to(snapshot_time); -// else -// notification_manager->push_notification( -// NotificationType::CustomSupportsAndSeamRemovedAfterRepair, -// NotificationManager::NotificationLevel::RegularNotification, -// _u8L("Cannot undo to before the mesh repair!")); -// return true; -// }); - } - fix_model_by_win10_sdk_gui(*mo, vol_idx); - sla::reproject_points_and_holes(mo); - this->update(); - this->object_list_changed(); - this->schedule_background_process(); -} - -void Plater::priv::simplify(const int obj_idx, const int vol_idx/* = -1*/) -{ - if (obj_idx < 0) - return; - - // Do not fix anything when a gizmo is open. There might be issues with updates - // and what is worse, the snapshot time would refer to the internal stack. - if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) { - notification_manager->push_notification( - NotificationType::CustomSupportsAndSeamRemovedAfterRepair, - NotificationManager::NotificationLevel::RegularNotification, - _u8L("ERROR: Please close all manipulators available from " - "the left toolbar before fixing the mesh.")); - return; - } - - // size_t snapshot_time = undo_redo_stack().active_snapshot_time(); - Plater::TakeSnapshot snapshot(q, _L("Symplify model")); - - // ToDo - - this->update(); - this->object_list_changed(); - this->schedule_background_process(); + q->changed_mesh(obj_idx); } void Plater::priv::set_current_panel(wxPanel* panel) @@ -6262,6 +6201,51 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology) return ret; } +void Plater::clear_before_change_mesh(int obj_idx) +{ + ModelObject* mo = model().objects[obj_idx]; + + // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh + // may be different and they would make no sense. + bool paint_removed = false; + for (ModelVolume* mv : mo->volumes) { + paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty(); + mv->supported_facets.clear(); + mv->seam_facets.clear(); + mv->mmu_segmentation_facets.clear(); + } + if (paint_removed) { + // snapshot_time is captured by copy so the lambda knows where to undo/redo to. + get_notification_manager()->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("Custom supports and seams were removed after repairing the mesh.")); +// _u8L("Undo the repair"), +// [this, snapshot_time](wxEvtHandler*){ +// // Make sure the snapshot is still available and that +// // we are in the main stack and not in a gizmo-stack. +// if (undo_redo_stack().has_undo_snapshot(snapshot_time) +// && q->canvas3D()->get_gizmos_manager().get_current() == nullptr) +// undo_redo_to(snapshot_time); +// else +// notification_manager->push_notification( +// NotificationType::CustomSupportsAndSeamRemovedAfterRepair, +// NotificationManager::NotificationLevel::RegularNotification, +// _u8L("Cannot undo to before the mesh repair!")); +// return true; +// }); + } +} + +void Plater::changed_mesh(int obj_idx) +{ + ModelObject* mo = model().objects[obj_idx]; + sla::reproject_points_and_holes(mo); + update(); + p->object_list_changed(); + p->schedule_background_process(); +} + void Plater::changed_object(int obj_idx) { if (obj_idx < 0) @@ -6343,7 +6327,6 @@ void Plater::suppress_background_process(const bool stop_background_process) } void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); } -void Plater::simplify(const int obj_idx, const int vol_idx/* = -1*/) { p->simplify(obj_idx, vol_idx); } void Plater::mirror(Axis axis) { p->mirror(axis); } void Plater::split_object() { p->split_object(); } void Plater::split_volume() { p->split_volume(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d58e6b9b12..61a2da93dd 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -230,13 +230,16 @@ public: void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); + + void clear_before_change_mesh(int obj_idx); + void changed_mesh(int obj_idx); + void changed_object(int obj_idx); void changed_objects(const std::vector& object_idxs); void schedule_background_process(bool schedule = true); bool is_background_process_update_scheduled() const; void suppress_background_process(const bool stop_background_process) ; void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); - void simplify(const int obj_idx, const int vol_idx = -1); void send_gcode(); void eject_drive(); diff --git a/src/slic3r/GUI/SimplificationDialog.cpp b/src/slic3r/GUI/SimplificationDialog.cpp deleted file mode 100644 index 780fc24075..0000000000 --- a/src/slic3r/GUI/SimplificationDialog.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "SimplificationDialog.hpp" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "GUI.hpp" -#include "GUI_App.hpp" -#include "format.hpp" -#include "wxExtensions.hpp" -#include "I18N.hpp" -#include "libslic3r/Utils.hpp" - - -namespace Slic3r { -namespace GUI { - -#define BORDER_W 10 - -SimplificationDialog::SimplificationDialog(wxWindow* parent) : - DPIDialog(parent, wxID_ANY, _L("Name of Dialog"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) -{ - SetFont(wxGetApp().normal_font()); - - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Some text") + ":"); - - wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 2, 1, 2); - grid_sizer->AddGrowableCol(1, 1); - grid_sizer->SetFlexibleDirection(wxBOTH); - - for (int i = 0; i < 3; i++) { - auto* text = new wxStaticText(this, wxID_ANY, _L("Text") + " " + std::to_string(i) + " :"); - -#ifdef _WIN32 - long style = wxBORDER_SIMPLE; -#else - long style = 0 -#endif - auto value = new wxTextCtrl(this, wxID_ANY, "Some Value", wxDefaultPosition, wxDefaultSize, style); - - grid_sizer->Add(text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); - grid_sizer->Add(value, 1, wxEXPAND | wxBOTTOM, 1); - } - - wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - this->Bind(wxEVT_BUTTON, &SimplificationDialog::OnOK, this, wxID_OK); - - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(grid_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); - - SetSizer(topSizer); - topSizer->SetSizeHints(this); - - wxGetApp().UpdateDlgDarkUI(this); - - this->CenterOnScreen(); -} - -SimplificationDialog::~SimplificationDialog() -{ -} - -void SimplificationDialog::on_dpi_changed(const wxRect& suggested_rect) -{ - const int& em = em_unit(); - msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - - const wxSize& size = wxSize(45 * em, 35 * em); - SetMinSize(size); - - Fit(); - Refresh(); -} - -void SimplificationDialog::OnOK(wxEvent& event) -{ - event.Skip(); -} - -}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SimplificationDialog.hpp b/src/slic3r/GUI/SimplificationDialog.hpp deleted file mode 100644 index 4c0210ee86..0000000000 --- a/src/slic3r/GUI/SimplificationDialog.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef slic3r_SimplificationDialog_hpp_ -#define slic3r_SimplificationDialog_hpp_ - -#include "GUI_Utils.hpp" - -namespace Slic3r { -namespace GUI { - -class SimplificationDialog : public DPIDialog -{ - void OnOK(wxEvent& event); - -public: - SimplificationDialog(wxWindow* parent); - ~SimplificationDialog(); - -protected: - void on_dpi_changed(const wxRect& suggested_rect) override; -}; - - -} // namespace GUI -} // namespace Slic3r - -#endif //slic3r_SimplificationDialog_hpp_ diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index e079cb3028..06ca3635e0 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -231,9 +231,10 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") } #include "test_utils.hpp" -TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") +TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") { TriangleMesh mesh = load_model("frog_legs.obj"); + //TriangleMesh mesh; load_obj("C:/Users/filip/Documents/models/scarecrow_torso.obj", &mesh); double original_volume = its_volume(mesh.its); uint32_t wanted_count = mesh.its.indices.size() * 0.05; REQUIRE_FALSE(mesh.empty()); diff --git a/tests/libslic3r/test_mutable_priority_queue.cpp b/tests/libslic3r/test_mutable_priority_queue.cpp index 37b2444321..ece655365b 100644 --- a/tests/libslic3r/test_mutable_priority_queue.cpp +++ b/tests/libslic3r/test_mutable_priority_queue.cpp @@ -339,3 +339,104 @@ TEST_CASE("Mutable priority queue - reshedule first", "[MutableSkipHeapPriorityQ } } } + +TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]") +{ + struct MyValue{ + int id; + float val; + }; + size_t count = 50000; + std::vector idxs(count, {0}); + std::vector dels(count, false); + auto q = make_miniheap_mutable_priority_queue( + [&](MyValue &v, size_t idx) { + idxs[v.id] = idx; + }, + [](MyValue &l, MyValue &r) { return l.val < r.val; }); + q.reserve(count); + for (size_t id = 0; id < count; id++) { + MyValue mv; + mv.id = id; + mv.val = rand(); + q.push(mv); + } + MyValue it = q.top(); // copy + q.pop(); + bool valid = (it.id != 0) && (idxs[0] < 3 * count); + CHECK(valid); +} + +TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]") +{ + struct MyValue { + int id; + float val; + }; + size_t count = 5000; + std::vector idxs(count, {0}); + std::vector dels(count, false); + auto q = make_miniheap_mutable_priority_queue( + [&](MyValue &v, size_t idx) { idxs[v.id] = idx; }, + [](MyValue &l, MyValue &r) { return l.val < r.val; }); + q.reserve(count); + + auto rand_val = [&]()->float { return (rand() % 53) / 10.f; }; + int ord = 0; + for (size_t id = 0; id < count; id++) { + MyValue mv; + mv.id = ord++; + mv.val = rand_val(); + q.push(mv); + } + auto check = [&]()->bool{ + for (size_t i = 0; i < idxs.size(); ++i) { + if (dels[i]) continue; + size_t qid = idxs[i]; + if (qid > 3*count) { + return false; + } + MyValue &mv = q[qid]; + if (mv.id != i) { + return false; // ERROR + } + } + return true; + }; + + CHECK(check()); // initial check + + auto get_valid_id = [&]()->int { + int id = 0; + do { + id = rand() % count; + } while (dels[id]); + return id; + }; + for (size_t i = 0; i < 100; i++) { + MyValue it = q.top(); // copy + q.pop(); + dels[it.id] = true; + CHECK(check()); + if (i % 20 == 0) { + it.val = rand_val(); + q.push(it); + dels[it.id] = false; + CHECK(check()); + continue; + } + + int id = get_valid_id(); + q.remove(idxs[id]); + dels[id] = true; + CHECK(check()); + for (size_t j = 0; j < 5; j++) { + int id = get_valid_id(); + size_t qid = idxs[id]; + MyValue &mv = q[qid]; + mv.val = rand_val(); + q.update(qid); + CHECK(check()); + } + } +} From 709ea1283e4e31a8e102b73cc0359455af44153a Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 19 Jul 2021 09:50:45 +0200 Subject: [PATCH 013/240] FIX: new imgui change input float --- src/slic3r/GUI/GUI_Factories.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index c745a57439..d494f84f0f 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -663,7 +663,7 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu) wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu) { - wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Symplify model"), "", + wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "", [](wxCommandEvent&) { obj_list()->simplify(); }, "", menu, []() {return plater()->can_simplify(); }, plater()); menu->AppendSeparator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index ce11813479..fc96f5035a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -92,7 +92,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); ImGui::SetNextItemWidth(80); - int precision = (c.wanted_percent > 10)? 0: ((c.wanted_percent > 1)? 1:2); + const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f"); float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { if (c.wanted_percent < 0.f) c.wanted_percent = 0.f; @@ -108,7 +108,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); m_imgui->disabled_begin(!c.use_error); - if (ImGui::InputFloat("(maximal quadric error)", &c.max_error, 0.01f, .1f, 2)) { + if (ImGui::InputFloat("(maximal quadric error)", &c.max_error, 0.01f, .1f, "%.2f")) { if (c.max_error < 0.f) c.max_error = 0.f; is_valid_result = false; } From c444ef81bdaf53bdadcc674edf47320f91d8b753 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 19 Jul 2021 10:12:19 +0200 Subject: [PATCH 014/240] Fix status function --- src/libslic3r/QuadricEdgeCollapse.cpp | 49 +---------------------- src/libslic3r/QuadricEdgeCollapse.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 1 - 3 files changed, 3 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index c5de1b0a7f..4976328c49 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -81,49 +81,6 @@ namespace QuadricEdgeCollapse { using namespace QuadricEdgeCollapse; -#ifdef NDEBUG -bool check_neighbors(TriangleInfos &t_infos, - Indices& indices, - VertexInfos & v_infos) -{ - std::vector t_counts(v_infos.size(), 0); - for (size_t i = 0; i < indices.size(); i++) { - TriangleInfo &t_info = t_infos[i]; - if (t_info.is_deleted()) continue; - Triangle &t = indices[i]; - for (size_t vidx : t) ++t_counts[vidx]; - } - - size_t prev_end = 0; - for (size_t i = 0; i < v_infos.size(); i++) { - VertexInfo &v_info = v_infos[i]; - if (v_info.is_deleted()) continue; - if (v_info.count != t_counts[i]) { - // no correct count - return false; - } - if (prev_end > v_info.start) { - // overlap of start - return false; - } - prev_end = v_info.start + v_info.count; - } - return true; -} - -bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { - float epsilon = 1.f; - for (size_t i = 0; i < 3; i++) { - if ((nv[i] > (v0[i] + epsilon) && nv[i] > (v1[i] + epsilon)) || - (nv[i] < (v0[i] - epsilon) && nv[i] < (v1[i] - epsilon))) { - return false; - } - } - return true; -} - -#endif // NDEBUG - void Slic3r::its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count, @@ -172,7 +129,8 @@ void Slic3r::its_quadric_edge_collapse( auto increase_status = [&]() { double reduced = (actual_triangle_count - triangle_count) / (double) count_triangle_to_reduce; - double status = (100 - status_init_size) * (1. - reduced); + double status = status_init_size + (100 - status_init_size) * + (1. - reduced); statusfn(static_cast(std::round(status))); }; // modulo for update status @@ -206,7 +164,6 @@ void Slic3r::its_quadric_edge_collapse( SymMat q(v_info0.q); q += v_info1.q; Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); - //assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1])); // set of triangle indices that change quadric uint32_t ti1 = (v_info0.count < v_info1.count)? find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : @@ -275,8 +232,6 @@ void Slic3r::its_quadric_edge_collapse( t_info1.set_deleted(); // triangle counter decrementation actual_triangle_count-=2; - - //assert(check_neighbors(t_infos, its.indices, v_infos)); } // compact triangle diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp index 3fcf61a08c..c7330bfc5f 100644 --- a/src/libslic3r/QuadricEdgeCollapse.hpp +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -17,7 +17,7 @@ namespace Slic3r { /// When nullptr then max float is used /// Output: Last used ErrorValue to collapse edge /// Could stop process of calculation. -/// Give a feed back to user about progress. +/// Give a feed back to user about progress. Values 1 - 100 void its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count = 0, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index fc96f5035a..a681c09bec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -49,7 +49,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; ModelVolume *act_volume = obj->volumes.front(); - // Check selection of new volume // Do not reselect object when processing if (act_volume != volume && state == State::settings) { From 25a48870754ff6fdd3b85956611974b9f05e2938 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 19 Jul 2021 15:46:41 +0200 Subject: [PATCH 015/240] QEC: When collapsing edge flip normal than check other edges in triangle Quadric is calculated with double precission of normal Fix calculation of normal for changed triangles --- src/libslic3r/QuadricEdgeCollapse.cpp | 217 +++++++++++------- src/libslic3r/SimplifyMeshImpl.hpp | 6 - src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 7 +- tests/libslic3r/test_indexed_triangle_set.cpp | 2 +- .../libslic3r/test_mutable_priority_queue.cpp | 4 +- 5 files changed, 139 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 4976328c49..0905253eb1 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -27,9 +27,11 @@ namespace QuadricEdgeCollapse { // merge information together - faster access during processing struct TriangleInfo { - Vec3f n; // normalized normal - speed up calcualtion of q and check flip + Vec3f n; // normalized normal - used for check when fliped + // range(0 .. 2), unsigned char min_index = 0; // identify edge for minimal Error -> lightweight Error structure + TriangleInfo() = default; bool is_deleted() const { return n.x() > 2.f; } void set_deleted() { n.x() = 3.f; } @@ -60,24 +62,30 @@ namespace QuadricEdgeCollapse { }; using CopyEdgeInfos = std::vector; - Vec3f create_normal(const Triangle &triangle, const Vertices &vertices); - double calculate_error(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices); - Vec3f calculate_vertex(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices); + Vec3d create_normal(const Triangle &triangle, const Vertices &vertices); + std::array create_vertices(uint32_t id_v1, uint32_t id_v2, const Vertices &vertices); + std::array vertices_error(const SymMat &q, const std::array &vertices); + double calculate_determinant(const SymMat &q); + double calculate_error(uint32_t id_v1, uint32_t id_v2, const SymMat & q, const Vertices &vertices); + Vec3f calculate_vertex(uint32_t id_v1, uint32_t id_v2, const SymMat & q, const Vertices &vertices); + Vec3d calculate_vertex(double det, const SymMat &q); // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number double vertex_error(const SymMat &q, const Vec3d &vertex); - SymMat create_quadric(const Triangle &t, const Vec3f& normal, const Vertices &vertices); + SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices); std::tuple init(const indexed_triangle_set &its); uint32_t find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); + // find edge with smallest error in triangle + Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos); Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index); void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, uint32_t ti); void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, uint32_t ti0, uint32_t ti1, uint32_t vi0, uint32_t vi1, uint32_t vi_top0, const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1); void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its); - } +} // namespace QuadricEdgeCollapse using namespace QuadricEdgeCollapse; @@ -150,6 +158,7 @@ void Slic3r::its_quadric_edge_collapse( uint32_t ti0 = e.triangle_index; TriangleInfo &t_info0 = t_infos[ti0]; if (t_info0.is_deleted()) continue; + assert(t_info0.min_index < 3); const Triangle &t0 = its.indices[ti0]; uint32_t vi0 = t0[t_info0.min_index]; @@ -171,10 +180,29 @@ void Slic3r::its_quadric_edge_collapse( if (is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) || is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) { - // IMPROVE1: what about other edges in triangle? - // IMPROVE2: check mpq top if it is ti1 with same edge - e.value = std::numeric_limits::max(); - // error is changed when surround edge is reduced + + // try other triangle's edge + Vec3d errors = calculate_3errors(t0, its.vertices, v_infos); + Vec3i ord = (errors[0] < errors[1]) ? + ((errors[0] < errors[2])? + ((errors[1] < errors[2]) ? Vec3i(0, 1, 2) : Vec3i(0, 2, 1)) : + Vec3i(2, 0, 1)): + ((errors[1] < errors[2])? + ((errors[0] < errors[2]) ? Vec3i(1, 0, 2) : Vec3i(1, 2, 0)) : + Vec3i(2, 1, 0)); + + if (t_info0.min_index == ord[0]) { + t_info0.min_index = ord[1]; + e.value = errors[t_info0.min_index]; + } else if (t_info0.min_index == ord[1]) { + t_info0.min_index = ord[2]; + e.value = errors[t_info0.min_index]; + } else { + // error is changed when surround edge is reduced + t_info0.min_index = 3; // bad index -> invalidate + e.value = maximal_error; + } + // IMPROVE: check mpq top if it is ti1 with same edge mpq.push(e); continue; } @@ -222,6 +250,7 @@ void Slic3r::its_quadric_edge_collapse( for (uint32_t ti : changed_triangle_indices) { size_t priority_queue_index = ti_2_mpqi[ti]; TriangleInfo& t_info = t_infos[ti]; + t_info.n = create_normal(its.indices[ti], its.vertices).cast(); // recalc normals mpq[priority_queue_index] = calculate_error(ti, its.indices[ti], its.vertices, v_infos, t_info.min_index); mpq.update(priority_queue_index); } @@ -239,69 +268,79 @@ void Slic3r::its_quadric_edge_collapse( if (max_error != nullptr) *max_error = last_collapsed_error; } -Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle, +Vec3d QuadricEdgeCollapse::create_normal(const Triangle &triangle, const Vertices &vertices) { - const Vec3f &v0 = vertices[triangle[0]]; - const Vec3f &v1 = vertices[triangle[1]]; - const Vec3f &v2 = vertices[triangle[2]]; + Vec3d v0 = vertices[triangle[0]].cast(); + Vec3d v1 = vertices[triangle[1]].cast(); + Vec3d v2 = vertices[triangle[2]].cast(); // n = triangle normal - Vec3f n = (v1 - v0).cross(v2 - v0); + Vec3d n = (v1 - v0).cross(v2 - v0); n.normalize(); return n; } -double QuadricEdgeCollapse::calculate_error(uint32_t id_v1, - uint32_t id_v2, - SymMat & q, - const Vertices &vertices) +double QuadricEdgeCollapse::calculate_determinant(const SymMat &q) { - double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); - if (abs(det) < std::numeric_limits::epsilon()) { - // can't divide by zero - const Vec3f &v0 = vertices[id_v1]; - const Vec3f &v1 = vertices[id_v2]; - Vec3d verts[3] = {v0.cast(), v1.cast(), Vec3d()}; - verts[2] = (verts[0] + verts[1]) / 2; - double errors[] = {vertex_error(q, verts[0]), - vertex_error(q, verts[1]), - vertex_error(q, verts[2])}; - return *std::min_element(std::begin(errors), std::end(errors)); - } + return q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); +} +Vec3d QuadricEdgeCollapse::calculate_vertex(double det, const SymMat &q) { double det_1 = -1 / det; double det_x = q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) double det_y = q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) double det_z = q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) - Vec3d vertex(det_1 * det_x, -det_1 * det_y, det_1 * det_z); + return Vec3d(det_1 * det_x, -det_1 * det_y, det_1 * det_z); +} + +std::array QuadricEdgeCollapse::create_vertices(uint32_t id_v1, uint32_t id_v2, const Vertices &vertices) +{ + Vec3d v0 = vertices[id_v1].cast(); + Vec3d v1 = vertices[id_v2].cast(); + Vec3d vm = (v0 + v1) / 2.; + return {v0, v1, vm}; +} + +std::array QuadricEdgeCollapse::vertices_error( + const SymMat &q, const std::array &vertices) +{ + return { + vertex_error(q, vertices[0]), + vertex_error(q, vertices[1]), + vertex_error(q, vertices[2])}; +} + +double QuadricEdgeCollapse::calculate_error(uint32_t id_v1, + uint32_t id_v2, + const SymMat & q, + const Vertices &vertices) +{ + double det = calculate_determinant(q); + if (abs(det) < std::numeric_limits::epsilon()) { + // can't divide by zero + auto verts = create_vertices(id_v1, id_v2, vertices); + auto errors = vertices_error(q, verts); + return *std::min_element(std::begin(errors), std::end(errors)); + } + Vec3d vertex = calculate_vertex(det, q); return vertex_error(q, vertex); } // similar as calculate error but focus on new vertex without calculation of error Vec3f QuadricEdgeCollapse::calculate_vertex(uint32_t id_v1, uint32_t id_v2, - SymMat & q, + const SymMat & q, const Vertices &vertices) { - double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); + double det = calculate_determinant(q); if (abs(det) < std::numeric_limits::epsilon()) { // can't divide by zero - const Vec3f &v0 = vertices[id_v1]; - const Vec3f &v1 = vertices[id_v2]; - Vec3d verts[3] = {v0.cast(), v1.cast(), Vec3d()}; - verts[2] = (verts[0] + verts[1]) / 2; - double errors[] = {vertex_error(q, verts[0]), - vertex_error(q, verts[1]), - vertex_error(q, verts[2])}; - auto mit = std::min_element(std::begin(errors), std::end(errors)); + auto verts = create_vertices(id_v1, id_v2, vertices); + auto errors = vertices_error(q, verts); + auto mit = std::min_element(std::begin(errors), std::end(errors)); return verts[mit - std::begin(errors)].cast(); } - - double det_1 = -1 / det; - double det_x = q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) - double det_y = q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) - double det_z = q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) - return Vec3f(det_1 * det_x, -det_1 * det_y, det_1 * det_z); + return calculate_vertex(det, q).cast(); } double QuadricEdgeCollapse::vertex_error(const SymMat &q, const Vec3d &vertex) @@ -312,11 +351,10 @@ double QuadricEdgeCollapse::vertex_error(const SymMat &q, const Vec3d &vertex) 2 * q[6] * y + q[7] * z * z + 2 * q[8] * z + q[9]; } -SymMat QuadricEdgeCollapse::create_quadric(const Triangle & t, - const Vec3f &normal, - const Vertices & vertices) +SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t, + const Vec3d & n, + const Vertices &vertices) { - Vec3d n = normal.cast(); Vec3d v0 = vertices[t[0]].cast(); return SymMat(n.x(), n.y(), n.z(), -n.dot(v0)); } @@ -326,29 +364,31 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) { TriangleInfos t_infos(its.indices.size()); VertexInfos v_infos(its.vertices.size()); - EdgeInfos e_infos(its.indices.size() * 3); - Errors errors(its.indices.size()); - // calculate normals - tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), - [&](const tbb::blocked_range &range) { - for (size_t i = range.begin(); i < range.end(); ++i) { - const Triangle &t = its.indices[i]; - TriangleInfo & t_info = t_infos[i]; - t_info.n = create_normal(t, its.vertices); - } - }); // END parallel for + { + std::vector triangle_quadrics(its.indices.size()); + // calculate normals + tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), + [&](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const Triangle &t = its.indices[i]; + TriangleInfo & t_info = t_infos[i]; + Vec3d normal = create_normal(t, its.vertices); + t_info.n = normal.cast(); + triangle_quadrics[i] = create_quadric(t, normal, its.vertices); + } + }); // END parallel for - // sum quadrics - for (size_t i = 0; i < its.indices.size(); i++) { - const Triangle &t = its.indices[i]; - TriangleInfo &t_info = t_infos[i]; - SymMat q = create_quadric(t, t_info.n, its.vertices); - for (size_t e = 0; e < 3; e++) { - VertexInfo &v_info = v_infos[t[e]]; - v_info.q += q; - ++v_info.count; // triangle count - } - } + // sum quadrics + for (size_t i = 0; i < its.indices.size(); i++) { + const Triangle &t = its.indices[i]; + const SymMat & q = triangle_quadrics[i]; + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos[t[e]]; + v_info.q += q; + ++v_info.count; // triangle count + } + } + } // remove triangle quadrics // set offseted starts uint32_t triangle_start = 0; @@ -361,6 +401,7 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) assert(its.indices.size() * 3 == triangle_start); // calc error + Errors errors(its.indices.size()); tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), [&](const tbb::blocked_range &range) { for (size_t i = range.begin(); i < range.end(); ++i) { @@ -371,6 +412,7 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) }); // END parallel for // create reference + EdgeInfos e_infos(its.indices.size() * 3); for (size_t i = 0; i < its.indices.size(); i++) { const Triangle &t = its.indices[i]; for (size_t j = 0; j < 3; ++j) { @@ -446,21 +488,30 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, return false; } +Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t, + const Vertices & vertices, + const VertexInfos &v_infos) +{ + Vec3d error; + for (size_t j = 0; j < 3; ++j) { + size_t j2 = (j == 2) ? 0 : (j + 1); + uint32_t vi0 = t[j]; + uint32_t vi1 = t[j2]; + SymMat q(v_infos[vi0].q); // copy + q += v_infos[vi1].q; + error[j] = calculate_error(vi0, vi1, q, vertices); + } + return error; +} + Error QuadricEdgeCollapse::calculate_error(uint32_t ti, const Triangle & t, const Vertices & vertices, const VertexInfos &v_infos, unsigned char & min_index) { - Vec3d error; - for (size_t j = 0; j < 3; ++j) { - size_t j2 = (j == 2) ? 0 : (j + 1); - uint32_t vi0 = t[j]; - uint32_t vi1 = t[j2]; - SymMat q(v_infos[vi0].q); // copy - q += v_infos[vi1].q; - error[j] = calculate_error(vi0, vi1, q, vertices); - } + Vec3d error = calculate_3errors(t, vertices, v_infos); + // select min error min_index = (error[0] < error[1]) ? ((error[0] < error[2]) ? 0 : 2) : ((error[1] < error[2]) ? 1 : 2); return Error(static_cast(error[min_index]), ti); diff --git a/src/libslic3r/SimplifyMeshImpl.hpp b/src/libslic3r/SimplifyMeshImpl.hpp index 0b3b9a2f64..d143b6d876 100644 --- a/src/libslic3r/SimplifyMeshImpl.hpp +++ b/src/libslic3r/SimplifyMeshImpl.hpp @@ -122,12 +122,6 @@ public: return *this; } - const SymetricMatrix &operator-=(const SymetricMatrix &n) - { - for (size_t i = 0; i < N; ++i) m[i] -= n[i]; - return *this; - } - SymetricMatrix operator+(const SymetricMatrix& n) { SymetricMatrix self = *this; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index a681c09bec..8e756a6397 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -69,10 +69,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; m_imgui->begin(_L("Simplify mesh ") + volume->name, flag); - std::string description = "Reduce amout of triangle in mesh( " + - volume->name + " has " + - std::to_string(triangle_count) + " triangles)"; - ImGui::Text(description.c_str()); + ImGui::Text("Reduce amout of triangle in mesh(%s has %d triangles", volume->name, triangle_count); // First initialization + fix triangle count if (c.wanted_count > triangle_count) c.update_percent(triangle_count); if (m_imgui->checkbox(_L("Until triangle count is less than "), c.use_count)) { @@ -202,7 +199,7 @@ void GLGizmoSimplify::process() float* max_error = (c.use_error)? &c.max_error : nullptr; std::function throw_on_cancel = [&]() { if ( state == State::canceling) throw SimplifyCanceledException(); }; std::function statusfn = [&](int percent) { progress = percent; }; - indexed_triangle_set collapsed = original_its.value(); // copy + indexed_triangle_set collapsed = *original_its; // copy try { its_quadric_edge_collapse(collapsed, triangle_count, max_error, throw_on_cancel, statusfn); set_its(collapsed); diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 06ca3635e0..195c32003a 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -125,7 +125,7 @@ static std::mt19937 create_random_generator() { std::vector its_sample_surface(const indexed_triangle_set &its, double sample_per_mm2, - std::mt19937 &random_generator = create_random_generator()) + std::mt19937 random_generator = create_random_generator()) { std::vector samples; std::uniform_real_distribution rand01(0.f, 1.f); diff --git a/tests/libslic3r/test_mutable_priority_queue.cpp b/tests/libslic3r/test_mutable_priority_queue.cpp index ece655365b..40ed5a1586 100644 --- a/tests/libslic3r/test_mutable_priority_queue.cpp +++ b/tests/libslic3r/test_mutable_priority_queue.cpp @@ -370,7 +370,7 @@ TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]" TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]") { struct MyValue { - int id; + size_t id; float val; }; size_t count = 5000; @@ -382,7 +382,7 @@ TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]") q.reserve(count); auto rand_val = [&]()->float { return (rand() % 53) / 10.f; }; - int ord = 0; + size_t ord = 0; for (size_t id = 0; id < count; id++) { MyValue mv; mv.id = ord++; From 2a2530924fc4d648f3c878feb0686335cd905516 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 20 Jul 2021 08:30:49 +0200 Subject: [PATCH 016/240] FIX for build Asan_OsX_Mojave_Universal_ARM64/590 ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:72:81: error: cannot pass non-trivial object of type 'std::string' (aka 'basic_string, allocator >') to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:72:87: warning: format specifies type 'int' but the argument has type 'size_t' (aka 'unsigned long') [-Wformat] --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 8e756a6397..b46c18a110 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -69,7 +69,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; m_imgui->begin(_L("Simplify mesh ") + volume->name, flag); - ImGui::Text("Reduce amout of triangle in mesh(%s has %d triangles", volume->name, triangle_count); + ImGui::Text(_L("Reduce amout of triangle in mesh(%s has %d triangles").c_str(), + volume->name.c_str(), static_cast(triangle_count)); // First initialization + fix triangle count if (c.wanted_count > triangle_count) c.update_percent(triangle_count); if (m_imgui->checkbox(_L("Until triangle count is less than "), c.use_count)) { From 6464a5b6985d3e40d86c1472923d9f34b6460574 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 20 Jul 2021 12:05:44 +0200 Subject: [PATCH 017/240] Fix Typo --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index b46c18a110..35b6e63cee 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -114,7 +114,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (state == State::settings) { if (m_imgui->button(_L("Cancel"))) { if (original_its.has_value()) { - set_its(*original_its); + set_its(*original_its); state = State::close_on_end; } else { close(); @@ -127,7 +127,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi process(); } ImGui::SameLine(); - if (m_imgui->button(_L("Aply"))) { + if (m_imgui->button(_L("Apply"))) { if (!is_valid_result) { state = State::close_on_end; process(); From b88629974d6b8cbe43d7242bfd8f173d423984ae Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 20 Jul 2021 15:07:48 +0200 Subject: [PATCH 018/240] Change UI Fix minimal triangle count Fix progress rendering --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 57 +++++++++++++++++------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 35b6e63cee..0edb1f5ce4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -68,27 +68,46 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; - m_imgui->begin(_L("Simplify mesh ") + volume->name, flag); - ImGui::Text(_L("Reduce amout of triangle in mesh(%s has %d triangles").c_str(), - volume->name.c_str(), static_cast(triangle_count)); + m_imgui->begin(on_get_name(), flag); + + int top_left_width = 100; + int bottom_left_width = 120; + int input_width = 80; + + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, + _L("Mesh name") + ":"); + ImGui::SameLine(top_left_width); + m_imgui->text(volume->name); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, + _L("Triangles") + ":"); + ImGui::SameLine(top_left_width); + m_imgui->text(std::to_string(volume->mesh().its.indices.size())); + + ImGui::Separator(); + + + ImGui::Text(_L("Limit by triangles").c_str()); + ImGui::SameLine(bottom_left_width); // First initialization + fix triangle count - if (c.wanted_count > triangle_count) c.update_percent(triangle_count); - if (m_imgui->checkbox(_L("Until triangle count is less than "), c.use_count)) { + if (m_imgui->checkbox("##UseCount", c.use_count)) { if (!c.use_count) c.use_error = true; is_valid_result = false; } m_imgui->disabled_begin(!c.use_count); - ImGui::SameLine(); + ImGui::Text(_L("Triangle count").c_str()); + ImGui::SameLine(bottom_left_width); int wanted_count = c.wanted_count; - if (ImGui::SliderInt("triangles", &wanted_count, 0, - triangle_count, "%d")) { + if (ImGui::SliderInt("##triangle_count", &wanted_count, 0, triangle_count, "%d")) { c.wanted_count = static_cast(wanted_count); + if (c.wanted_count < 4) c.wanted_count = 4; + if (c.wanted_count > triangle_count) c.wanted_count = triangle_count; c.update_count(triangle_count); is_valid_result = false; } - ImGui::SameLine(); - ImGui::SetNextItemWidth(80); + ImGui::Text(_L("Ratio").c_str()); + ImGui::SameLine(bottom_left_width); + ImGui::SetNextItemWidth(input_width); const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f"); float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { @@ -99,13 +118,19 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } m_imgui->disabled_end(); // use_count - if (m_imgui->checkbox(_L("Until error"), c.use_error)) { + ImGui::NewLine(); + ImGui::Text(_L("Limit by error").c_str()); + ImGui::SameLine(bottom_left_width); + if (m_imgui->checkbox("##UseError", c.use_error)) { if (!c.use_error) c.use_count = true; is_valid_result = false; } - ImGui::SameLine(); + m_imgui->disabled_begin(!c.use_error); - if (ImGui::InputFloat("(maximal quadric error)", &c.max_error, 0.01f, .1f, "%.2f")) { + ImGui::Text(_L("Max. error").c_str()); + ImGui::SameLine(bottom_left_width); + ImGui::SetNextItemWidth(input_width); + if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) { if (c.max_error < 0.f) c.max_error = 0.f; is_valid_result = false; } @@ -199,7 +224,10 @@ void GLGizmoSimplify::process() uint32_t triangle_count = (c.use_count)? c.wanted_count : 0; float* max_error = (c.use_error)? &c.max_error : nullptr; std::function throw_on_cancel = [&]() { if ( state == State::canceling) throw SimplifyCanceledException(); }; - std::function statusfn = [&](int percent) { progress = percent; }; + std::function statusfn = [&](int percent) { + progress = percent; + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + }; indexed_triangle_set collapsed = *original_its; // copy try { its_quadric_edge_collapse(collapsed, triangle_count, max_error, throw_on_cancel, statusfn); @@ -208,6 +236,7 @@ void GLGizmoSimplify::process() } catch (SimplifyCanceledException &) { is_valid_result = false; } + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }); } From ca3fc06e363447d46ba557ee1dc93dae56c85ca2 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 20 Jul 2021 18:39:09 +0200 Subject: [PATCH 019/240] GUI edits --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 102 ++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 4 +- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 0edb1f5ce4..07a08de9ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -44,6 +44,18 @@ void GLGizmoSimplify::on_render_for_picking() const{} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { + const int min_triangle_count = 4; // tetrahedron + // GUI size constants + const int top_left_width = 100; + const int bottom_left_width = 100; + const int input_width = 100; + const int input_small_width = 80; + const int window_offset = 100; + + int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse; + m_imgui->begin(on_get_name(), flag); + const Selection &selection = m_parent.get_selection(); int object_idx = selection.get_object_idx(); ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; @@ -59,6 +71,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi c.wanted_percent = 50.; // default value c.update_percent(tm.its.indices.size()); is_valid_result = false; + // set window position + ImVec2 pos = ImGui::GetMousePos(); + pos.x -= window_offset; + pos.y -= window_offset; + ImGui::SetWindowPos(pos, ImGuiCond_Always); } size_t triangle_count = volume->mesh().its.indices.size(); @@ -66,26 +83,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (original_its.has_value()) triangle_count = original_its->indices.size(); - int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoCollapse; - m_imgui->begin(on_get_name(), flag); - - int top_left_width = 100; - int bottom_left_width = 120; - int input_width = 80; - - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, - _L("Mesh name") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); ImGui::SameLine(top_left_width); m_imgui->text(volume->name); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, - _L("Triangles") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); ImGui::SameLine(top_left_width); - m_imgui->text(std::to_string(volume->mesh().its.indices.size())); + m_imgui->text(std::to_string(triangle_count)); ImGui::Separator(); - ImGui::Text(_L("Limit by triangles").c_str()); ImGui::SameLine(bottom_left_width); // First initialization + fix triangle count @@ -98,22 +104,28 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::Text(_L("Triangle count").c_str()); ImGui::SameLine(bottom_left_width); int wanted_count = c.wanted_count; - if (ImGui::SliderInt("##triangle_count", &wanted_count, 0, triangle_count, "%d")) { + ImGui::SetNextItemWidth(input_width); + if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) { c.wanted_count = static_cast(wanted_count); - if (c.wanted_count < 4) c.wanted_count = 4; - if (c.wanted_count > triangle_count) c.wanted_count = triangle_count; + if (c.wanted_count < min_triangle_count) + c.wanted_count = min_triangle_count; + if (c.wanted_count > triangle_count) + c.wanted_count = triangle_count; c.update_count(triangle_count); is_valid_result = false; } ImGui::Text(_L("Ratio").c_str()); ImGui::SameLine(bottom_left_width); - ImGui::SetNextItemWidth(input_width); + ImGui::SetNextItemWidth(input_small_width); const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f"); float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { - if (c.wanted_percent < 0.f) c.wanted_percent = 0.f; if (c.wanted_percent > 100.f) c.wanted_percent = 100.f; c.update_percent(triangle_count); + if (c.wanted_count < min_triangle_count) { + c.wanted_count = min_triangle_count; + c.update_count(triangle_count); + } is_valid_result = false; } m_imgui->disabled_end(); // use_count @@ -129,13 +141,14 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(!c.use_error); ImGui::Text(_L("Max. error").c_str()); ImGui::SameLine(bottom_left_width); - ImGui::SetNextItemWidth(input_width); + ImGui::SetNextItemWidth(input_small_width); if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) { if (c.max_error < 0.f) c.max_error = 0.f; is_valid_result = false; } m_imgui->disabled_end(); // use_error + if (state == State::settings) { if (m_imgui->button(_L("Cancel"))) { if (original_its.has_value()) { @@ -145,7 +158,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi close(); } } - ImGui::SameLine(); + ImGui::SameLine(bottom_left_width); if (m_imgui->button(_L("Preview"))) { state = State::simplifying; // simplify but not aply on mesh @@ -166,10 +179,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_imgui->button(_L("Cancel"))) state = State::canceling; m_imgui->disabled_end(); - ImGui::SameLine(); + ImGui::SameLine(bottom_left_width); // draw progress bar - ImGui::Text("Processing %c \t %d / 100", - "|/-\\"[(int) (ImGui::GetTime() / 0.05f) & 3], progress); + char buf[32]; + sprintf(buf, L("Process %d / 100"), progress); + ImGui::ProgressBar(progress / 100., ImVec2(input_width, 0.f), buf); } m_imgui->end(); @@ -190,8 +204,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } // change from simplifying | aply - state = State::settings; - + state = State::settings; } } @@ -211,9 +224,9 @@ void GLGizmoSimplify::process() const char* what() const throw() { return L("Model simplification has been canceled"); } }; - if (!original_its.has_value()) + if (!original_its.has_value()) original_its = volume->mesh().its; // copy - + auto plater = wxGetApp().plater(); plater->take_snapshot(_L("Simplify ") + volume->name); plater->clear_before_change_mesh(obj_index); @@ -221,20 +234,41 @@ void GLGizmoSimplify::process() if (worker.joinable()) worker.join(); worker = std::thread([&]() { // store original triangles - uint32_t triangle_count = (c.use_count)? c.wanted_count : 0; - float* max_error = (c.use_error)? &c.max_error : nullptr; - std::function throw_on_cancel = [&]() { if ( state == State::canceling) throw SimplifyCanceledException(); }; + uint32_t triangle_count = (c.use_count) ? c.wanted_count : 0; + float max_error = (c.use_error) ? c.max_error : std::numeric_limits::max(); + + std::function throw_on_cancel = [&]() { + if (state == State::canceling) { + throw SimplifyCanceledException(); + } + }; std::function statusfn = [&](int percent) { progress = percent; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }; - indexed_triangle_set collapsed = *original_its; // copy + + indexed_triangle_set collapsed; + if (last_error.has_value()) { + // is chance to continue with last reduction + const indexed_triangle_set &its = volume->mesh().its; + uint32_t last_triangle_count = static_cast(its.indices.size()); + if ((!c.use_count || triangle_count <= last_triangle_count) && + (!c.use_error || c.max_error <= *last_error)) { + collapsed = its; // small copy + } else { + collapsed = *original_its; // copy + } + } else { + collapsed = *original_its; // copy + } + try { - its_quadric_edge_collapse(collapsed, triangle_count, max_error, throw_on_cancel, statusfn); + its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); is_valid_result = true; + last_error = max_error; } catch (SimplifyCanceledException &) { - is_valid_result = false; + state = State::settings; } wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 299d559c77..bc0bd7c5eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -20,19 +20,19 @@ class GLGizmoSimplify : public GLGizmoBase } state; bool is_valid_result; // differ what to do in apply - int progress; ModelVolume *volume; size_t obj_index; std::optional original_its; + std::optional last_error; // for use previous reduction + bool need_reload; // after simplify, glReload must be on main thread std::thread worker; public: GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); virtual ~GLGizmoSimplify(); - void set_selectable(bool value); protected: virtual bool on_init() override; virtual std::string on_get_name() const override; From 6bcc576b5fbcc869f2db39af32f276366d71a0b4 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 21 Jul 2021 08:34:43 +0200 Subject: [PATCH 020/240] truncate model name --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 7 ++++++- tests/libslic3r/test_indexed_triangle_set.cpp | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 07a08de9ca..fc53490d7b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -46,11 +46,13 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi { const int min_triangle_count = 4; // tetrahedron // GUI size constants + // TODO: calculate from translation text sizes const int top_left_width = 100; const int bottom_left_width = 100; const int input_width = 100; const int input_small_width = 80; const int window_offset = 100; + const int max_char_in_name = 25; int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; @@ -85,7 +87,10 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); ImGui::SameLine(top_left_width); - m_imgui->text(volume->name); + std::string name = volume->name; + if (name.length() > max_char_in_name) + name = name.substr(0, max_char_in_name-3) + "..."; + m_imgui->text(name); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); ImGui::SameLine(top_left_width); m_imgui->text(std::to_string(triangle_count)); diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 195c32003a..b640d8410c 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -233,8 +233,7 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") #include "test_utils.hpp" TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") { - TriangleMesh mesh = load_model("frog_legs.obj"); - //TriangleMesh mesh; load_obj("C:/Users/filip/Documents/models/scarecrow_torso.obj", &mesh); + TriangleMesh mesh = load_model("frog_legs.obj"); double original_volume = its_volume(mesh.its); uint32_t wanted_count = mesh.its.indices.size() * 0.05; REQUIRE_FALSE(mesh.empty()); From 0079091a8c74e1dd70a9be8945e24e76d1043567 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 21 Jul 2021 11:58:01 +0200 Subject: [PATCH 021/240] Fix gui size --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 61 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 15 +++++- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index fc53490d7b..1d98c9de33 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -34,6 +34,7 @@ bool GLGizmoSimplify::on_init() return true; } + std::string GLGizmoSimplify::on_get_name() const { return (_L("Simplify")).ToUTF8().data(); @@ -45,14 +46,8 @@ void GLGizmoSimplify::on_render_for_picking() const{} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { const int min_triangle_count = 4; // tetrahedron - // GUI size constants - // TODO: calculate from translation text sizes - const int top_left_width = 100; - const int bottom_left_width = 100; - const int input_width = 100; - const int input_small_width = 80; - const int window_offset = 100; const int max_char_in_name = 25; + create_gui_cfg(); int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; @@ -75,8 +70,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi is_valid_result = false; // set window position ImVec2 pos = ImGui::GetMousePos(); - pos.x -= window_offset; - pos.y -= window_offset; + pos.x -= gui_cfg->window_offset; + pos.y -= gui_cfg->window_offset; ImGui::SetWindowPos(pos, ImGuiCond_Always); } @@ -86,19 +81,19 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi triangle_count = original_its->indices.size(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); - ImGui::SameLine(top_left_width); + ImGui::SameLine(gui_cfg->top_left_width); std::string name = volume->name; if (name.length() > max_char_in_name) name = name.substr(0, max_char_in_name-3) + "..."; m_imgui->text(name); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); - ImGui::SameLine(top_left_width); + ImGui::SameLine(gui_cfg->top_left_width); m_imgui->text(std::to_string(triangle_count)); ImGui::Separator(); ImGui::Text(_L("Limit by triangles").c_str()); - ImGui::SameLine(bottom_left_width); + ImGui::SameLine(gui_cfg->bottom_left_width); // First initialization + fix triangle count if (m_imgui->checkbox("##UseCount", c.use_count)) { if (!c.use_count) c.use_error = true; @@ -107,9 +102,9 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(!c.use_count); ImGui::Text(_L("Triangle count").c_str()); - ImGui::SameLine(bottom_left_width); + ImGui::SameLine(gui_cfg->bottom_left_width); int wanted_count = c.wanted_count; - ImGui::SetNextItemWidth(input_width); + ImGui::SetNextItemWidth(gui_cfg->input_width); if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) { c.wanted_count = static_cast(wanted_count); if (c.wanted_count < min_triangle_count) @@ -120,8 +115,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi is_valid_result = false; } ImGui::Text(_L("Ratio").c_str()); - ImGui::SameLine(bottom_left_width); - ImGui::SetNextItemWidth(input_small_width); + ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(gui_cfg->input_small_width); const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f"); float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { @@ -137,7 +132,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::NewLine(); ImGui::Text(_L("Limit by error").c_str()); - ImGui::SameLine(bottom_left_width); + ImGui::SameLine(gui_cfg->bottom_left_width); if (m_imgui->checkbox("##UseError", c.use_error)) { if (!c.use_error) c.use_count = true; is_valid_result = false; @@ -145,8 +140,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(!c.use_error); ImGui::Text(_L("Max. error").c_str()); - ImGui::SameLine(bottom_left_width); - ImGui::SetNextItemWidth(input_small_width); + ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(gui_cfg->input_small_width); if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) { if (c.max_error < 0.f) c.max_error = 0.f; is_valid_result = false; @@ -163,7 +158,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi close(); } } - ImGui::SameLine(bottom_left_width); + ImGui::SameLine(gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { state = State::simplifying; // simplify but not aply on mesh @@ -184,11 +179,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_imgui->button(_L("Cancel"))) state = State::canceling; m_imgui->disabled_end(); - ImGui::SameLine(bottom_left_width); + ImGui::SameLine(gui_cfg->bottom_left_width); // draw progress bar char buf[32]; sprintf(buf, L("Process %d / 100"), progress); - ImGui::ProgressBar(progress / 100., ImVec2(input_width, 0.f), buf); + ImGui::ProgressBar(progress / 100., ImVec2(gui_cfg->input_width, 0.f), buf); } m_imgui->end(); @@ -293,4 +288,26 @@ bool GLGizmoSimplify::on_is_activable() const return !m_parent.get_selection().is_empty(); } +void GLGizmoSimplify::create_gui_cfg() { + if (gui_cfg.has_value()) return; + + int space_size = m_imgui->calc_text_size(":MM").x; + GuiCfg cfg; + cfg.top_left_width = std::max(m_imgui->calc_text_size(_L("Mesh name")).x, + m_imgui->calc_text_size(_L("Triangles")).x) + + space_size; + + cfg.bottom_left_width = + std::max( + std::max(m_imgui->calc_text_size(_L("Limit by triangles")).x, + std::max(m_imgui->calc_text_size(_L("Triangle count")).x, + m_imgui->calc_text_size(_L("Ratio")).x)), + std::max(m_imgui->calc_text_size(_L("Limit by error")).x, + m_imgui->calc_text_size(_L("Max. error")).x)) + space_size; + cfg.input_width = cfg.bottom_left_width; + cfg.input_small_width = cfg.input_width * 0.8; + cfg.window_offset = cfg.input_width; + gui_cfg = cfg; +} + } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index bc0bd7c5eb..06461d222a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -66,8 +66,21 @@ private: wanted_count = static_cast( std::round(triangle_count * wanted_percent / 100.f)); } + } c; + + // This configs holds GUI layout size given by translated texts. + // etc. When language changes, GUI is recreated and this class constructed again, + // so the change takes effect. (info by GLGizmoFdmSupports.hpp) + struct GuiCfg + { + int top_left_width = 100; + int bottom_left_width = 100; + int input_width = 100; + int input_small_width = 80; + int window_offset = 100; }; - Configuration c; + std::optional gui_cfg; + void create_gui_cfg(); }; } // namespace GUI From b225fadfdee858c11a746ef56c6653e28573fd41 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 21 Jul 2021 15:23:48 +0200 Subject: [PATCH 022/240] Allow skip edge with only one triangle during simplification --- src/libslic3r/QuadricEdgeCollapse.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index 0905253eb1..e42ed5deb2 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -73,7 +73,8 @@ namespace QuadricEdgeCollapse { double vertex_error(const SymMat &q, const Vec3d &vertex); SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices); std::tuple init(const indexed_triangle_set &its); - uint32_t find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); + std::optional find_triangle_index1(uint32_t vi, const VertexInfo& v_info, + uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); @@ -174,13 +175,12 @@ void Slic3r::its_quadric_edge_collapse( q += v_info1.q; Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); // set of triangle indices that change quadric - uint32_t ti1 = (v_info0.count < v_info1.count)? + auto ti1_opt = (v_info0.count < v_info1.count)? find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; - - if (is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) || - is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) { - + if (!ti1_opt.has_value() || // edge has only one triangle + is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) || + is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) { // try other triangle's edge Vec3d errors = calculate_3errors(t0, its.vertices, v_infos); Vec3i ord = (errors[0] < errors[1]) ? @@ -190,7 +190,6 @@ void Slic3r::its_quadric_edge_collapse( ((errors[1] < errors[2])? ((errors[0] < errors[2]) ? Vec3i(1, 0, 2) : Vec3i(1, 2, 0)) : Vec3i(2, 1, 0)); - if (t_info0.min_index == ord[0]) { t_info0.min_index = ord[1]; e.value = errors[t_info0.min_index]; @@ -206,6 +205,7 @@ void Slic3r::its_quadric_edge_collapse( mpq.push(e); continue; } + uint32_t ti1 = *ti1_opt; last_collapsed_error = e.value; changed_triangle_indices.clear(); changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4); @@ -428,7 +428,7 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) return {t_infos, v_infos, e_infos, errors}; } -uint32_t QuadricEdgeCollapse::find_triangle_index1(uint32_t vi, +std::optional QuadricEdgeCollapse::find_triangle_index1(uint32_t vi, const VertexInfo &v_info, uint32_t ti0, const EdgeInfos & e_infos, @@ -445,8 +445,7 @@ uint32_t QuadricEdgeCollapse::find_triangle_index1(uint32_t vi, return e_info.t_index; } // triangle0 is on border and do NOT have twin edge - assert(false); - return -1; + return {}; } bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, From 72d01d75e462ed8b4eceb669b15dde2e4080f33d Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 21 Jul 2021 15:48:10 +0200 Subject: [PATCH 023/240] FIX schedule_extra_frame on end; --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 1d98c9de33..ad08374ba0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -148,7 +148,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } m_imgui->disabled_end(); // use_error - if (state == State::settings) { if (m_imgui->button(_L("Cancel"))) { if (original_its.has_value()) { @@ -243,8 +242,8 @@ void GLGizmoSimplify::process() } }; std::function statusfn = [&](int percent) { - progress = percent; - wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + progress = percent; + m_parent.schedule_extra_frame(0); }; indexed_triangle_set collapsed; @@ -270,7 +269,10 @@ void GLGizmoSimplify::process() } catch (SimplifyCanceledException &) { state = State::settings; } - wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + // need to render last status fn + // without Sleep it freeze until mouse move + Sleep(50); + m_parent.schedule_extra_frame(0); }); } From c0b73a92a3e42881852b71836ac96f26645883a5 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Thu, 22 Jul 2021 15:17:23 +0200 Subject: [PATCH 024/240] Try fix warning icon --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index ad08374ba0..b8884d995a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -4,6 +4,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/QuadricEdgeCollapse.hpp" @@ -203,7 +204,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } // change from simplifying | aply - state = State::settings; + state = State::settings; + + // Fix warning icon in object list + wxGetApp().obj_list()->update_item_error_icon(obj_index, -1); + wxGetApp().obj_list()->part_selection_changed(); } } From 4a5496e3de97e8579d161917de74411aaf7bea8f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 23 Jul 2021 13:48:38 +0200 Subject: [PATCH 025/240] ObjectDataViewModel: Added function to add warning icon --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 1 - src/slic3r/GUI/ObjectDataViewModel.cpp | 18 ++++++++++++++++++ src/slic3r/GUI/ObjectDataViewModel.hpp | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4b7cdf3c2b..45d7115e99 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3951,6 +3951,8 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co // unmark fixed item only m_objects_model->DeleteWarningIcon(item); } + else + m_objects_model->AddWarningIcon(item); } void ObjectList::msw_rescale() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index b8884d995a..22b3115270 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -208,7 +208,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // Fix warning icon in object list wxGetApp().obj_list()->update_item_error_icon(obj_index, -1); - wxGetApp().obj_list()->part_selection_changed(); } } diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 3eb0cd5c9b..ec09731c53 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1696,6 +1696,24 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty return *bmp; } +void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item) +{ + if (!item.IsOk()) + return; + ObjectDataViewModelNode *node = static_cast(item.GetID()); + + if (node->GetType() & itObject) { + node->SetBitmap(m_warning_bmp); + return; + } + + if (node->GetType() & itVolume) { + node->SetBitmap(GetVolumeIcon(node->GetVolumeType(), true)); + node->GetParent()->SetBitmap(m_warning_bmp); + return; + } +} + void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/) { if (!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index da251ef849..7d3b132bac 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -375,6 +375,7 @@ public: wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked = false); + void AddWarningIcon(const wxDataViewItem& item); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; From fc4b18ebb1b27606114d5444764829c796aef666 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 2 Aug 2021 10:46:47 +0200 Subject: [PATCH 026/240] Merge master --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 22b3115270..92654c6064 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -41,8 +41,8 @@ std::string GLGizmoSimplify::on_get_name() const return (_L("Simplify")).ToUTF8().data(); } -void GLGizmoSimplify::on_render() const{} -void GLGizmoSimplify::on_render_for_picking() const{} +void GLGizmoSimplify::on_render() {} +void GLGizmoSimplify::on_render_for_picking() {} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 06461d222a..49c3e8dbda 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -36,8 +36,8 @@ public: protected: virtual bool on_init() override; virtual std::string on_get_name() const override; - virtual void on_render() const override; - virtual void on_render_for_picking() const override; + virtual void on_render() override; + virtual void on_render_for_picking() override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual bool on_is_activable() const override; virtual bool on_is_selectable() const override { return false; }; From e76b54b7708d03bcaece1fa9303d52229aff60cc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 4 Aug 2021 15:43:22 +0200 Subject: [PATCH 027/240] Follow-up of bc81c22e (renamed the new CLI option --dont-ensure-on-bed to avoid double negatives) --- src/PrusaSlicer.cpp | 9 +++------ src/libslic3r/PrintConfig.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 21b161456f..3490b81836 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -258,7 +258,6 @@ int CLI::run(int argc, char **argv) Points bed = get_bed_shape(m_print_config); ArrangeParams arrange_cfg; arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); - bool user_ensure_on_bed = true; for (auto const &opt_key : m_transforms) { if (opt_key == "merge") { @@ -331,10 +330,8 @@ int CLI::run(int argc, char **argv) } } else if (opt_key == "dont_arrange") { // do nothing - this option alters other transform options - } else if (opt_key == "dont_ensure_on_bed") { - // Remember that we saw this so we don't lift objects from the bed - // after the other transformations are processed. - user_ensure_on_bed = false; + } else if (opt_key == "ensure_on_bed") { + // do nothing, the value is used later } else if (opt_key == "rotate") { for (auto &model : m_models) for (auto &o : model.objects) @@ -439,7 +436,7 @@ int CLI::run(int argc, char **argv) // All transforms have been dealt with. Now ensure that the objects are on bed. // (Unless the user said otherwise.) - if (user_ensure_on_bed) + if (m_config.opt_bool("ensure_on_bed")) for (auto &model : m_models) for (auto &o : model.objects) o->ensure_on_bed(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 16c16f038e..ed7961ce10 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4154,9 +4154,10 @@ CLITransformConfigDef::CLITransformConfigDef() def->label = L("Don't arrange"); def->tooltip = L("Do not rearrange the given models before merging and keep their original XY coordinates."); - def = this->add("dont_ensure_on_bed", coBool); - def->label = L("Don't ensure on bed"); - def->tooltip = L("Do not lift the object above the bed when it is partially below."); + def = this->add("ensure_on_bed", coBool); + def->label = L("Ensure on bed"); + def->tooltip = L("Lift the object above the bed when it is partially below. Enabled by default, use --no-ensure-on-bed to disable."); + def->set_default_value(new ConfigOptionBool(true)); def = this->add("duplicate", coInt); def->label = L("Duplicate"); From 215ee293ae705943e28ab1509f50d03c85dd26a0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 5 Aug 2021 13:02:54 +0200 Subject: [PATCH 028/240] CLI parsing: allow giving explicit values for bool options, improved error reporting: It is now possible to use e.g. --ensure-on-bed=0 for bools (meaning the same as --no-ensure-on-bed). Using --no- prefix on non-boolean is an error (--no-ensure-on-bed=1) Providing a value for --no- prefixed bool is an error (--no-loglevel 5) --- src/libslic3r/Config.cpp | 45 +++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index b9f9b266dc..cbc434b395 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -871,6 +871,7 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option token.erase(equals_pos); } } + // Look for the cli -> option mapping. const auto it = opts.find(token); if (it == opts.end()) { @@ -879,15 +880,46 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option } const t_config_option_key opt_key = it->second; const ConfigOptionDef &optdef = this->def()->options.at(opt_key); + // If the option type expects a value and it was not already provided, // look for it in the next token. - if (optdef.type != coBool && optdef.type != coBools && value.empty()) { - if (i == (argc-1)) { - boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; + if (value.empty()) { + if (optdef.type != coBool && optdef.type != coBools) { + if (i == (argc-1)) { + boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; + return false; + } + value = argv[++ i]; + } else { + // This is a bool or bools. The value is optional, but may still be there. + // Check if the next token can be deserialized into ConfigOptionBool. + // If it is in fact bools, it will be rejected later anyway. + if (i != argc-1) { // There is still a token to read. + ConfigOptionBool cobool; + if (cobool.deserialize(argv[i+1])) + value = argv[++i]; + } + } + + } + + if (no) { + if (optdef.type != coBool && optdef.type != coBools) { + boost::nowide::cerr << "Only boolean config options can be negated with --no- prefix." << std::endl; + return false; + } + else if (! value.empty()) { + boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl; return false; } - value = argv[++ i]; } + if (optdef.type == coBools && ! value.empty()) { + boost::nowide::cerr << "Vector boolean options cannot have a value. Fill them in by " + "repeating them and negate by --no- prefix." << std::endl; + return false; + } + + // Store the option value. const bool existing = this->has(opt_key); if (keys != nullptr && ! existing) { @@ -911,7 +943,10 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option // unescaped by the calling shell. opt_vector->deserialize(value, true); } else if (opt_base->type() == coBool) { - static_cast(opt_base)->value = !no; + if (value.empty()) + static_cast(opt_base)->value = !no; + else + opt_base->deserialize(value); } else if (opt_base->type() == coString) { // Do not unescape single string values, the unescaping is left to the calling shell. static_cast(opt_base)->value = value; From 413dc8d6ec1630573ab159cef899599fd755816f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 5 Aug 2021 15:18:18 +0200 Subject: [PATCH 029/240] Revert "Restoring custom supports/seams after reload from disk" This reverts commit d001195ebd2239ba21f31a908d1c146e7af03c08. It makes no sense, reload from disk is used when the file has changed, which means the paint-on data are possibly meaningless or even completely wrong (referencing triangles that no longer exist) --- src/slic3r/GUI/Plater.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 723c93da21..665ae80880 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3423,9 +3423,6 @@ void Plater::priv::reload_from_disk() new_volume->convert_from_imperial_units(); if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); - new_volume->supported_facets.assign(old_volume->supported_facets); - new_volume->seam_facets.assign(old_volume->seam_facets); - new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); if (!sinking) From 49fdf3da7b32bf0dfaa8f199fc20c76148825f42 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 5 Aug 2021 15:35:44 +0200 Subject: [PATCH 030/240] follow-up to a86e7107a5d4549cf313c4097f87c309c3ff4d2c: Make is_converted_from_meters / is_converted_from_inches exclusive-or. Maybe it would be better to make a single enum from the two booleans, if they are exclusive-or? --- src/libslic3r/Format/3mf.cpp | 3 ++- src/libslic3r/Format/AMF.cpp | 3 ++- src/libslic3r/Model.cpp | 27 +++++++++++++++++++-------- src/slic3r/GUI/Plater.cpp | 10 ++++++---- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 613b7444af..16d86ac284 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2860,9 +2860,10 @@ namespace Slic3r { stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n"; stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n"; } + assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters); if (volume->source.is_converted_from_inches) stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n"; - if (volume->source.is_converted_from_meters) + else if (volume->source.is_converted_from_meters) stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n"; } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index d03cfd4fa0..0312c7f225 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -1241,9 +1241,10 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, stream << " " << volume->source.mesh_offset(1) << "\n"; stream << " " << volume->source.mesh_offset(2) << "\n"; } + assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters); if (volume->source.is_converted_from_inches) stream << " 1\n"; - if (volume->source.is_converted_from_meters) + else if (volume->source.is_converted_from_meters) stream << " 1\n"; stream << std::setprecision(std::numeric_limits::max_digits10); const indexed_triangle_set &its = volume->mesh().its; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ba743f49ca..c45acc0484 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -460,13 +460,15 @@ void Model::convert_multipart_object(unsigned int max_extruders) this->objects.push_back(object); } +static constexpr const double volume_threshold_inches = 9.0; // 9 = 3*3*3; + bool Model::looks_like_imperial_units() const { if (this->objects.size() == 0) return false; for (ModelObject* obj : this->objects) - if (obj->get_object_stl_stats().volume < 9.0) // 9 = 3*3*3; + if (obj->get_object_stl_stats().volume < volume_threshold_inches) return true; return false; @@ -474,22 +476,26 @@ bool Model::looks_like_imperial_units() const void Model::convert_from_imperial_units(bool only_small_volumes) { - double in_to_mm = 25.4; + static constexpr const in_to_mm = 25.4; for (ModelObject* obj : this->objects) - if (! only_small_volumes || obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3; + if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) { obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); - for (ModelVolume* v : obj->volumes) + for (ModelVolume* v : obj->volumes) { + assert(! v->source.is_converted_from_meters); v->source.is_converted_from_inches = true; + } } } +static constexpr const double volume_threshold_meters = 0.001; // 0.001 = 0.1*0.1*0.1 + bool Model::looks_like_saved_in_meters() const { if (this->objects.size() == 0) return false; for (ModelObject* obj : this->objects) - if (obj->get_object_stl_stats().volume < 0.001) // 0.001 = 0.1*0.1*0.1; + if (obj->get_object_stl_stats().volume < volume_threshold_meters) return true; return false; @@ -497,12 +503,14 @@ bool Model::looks_like_saved_in_meters() const void Model::convert_from_meters(bool only_small_volumes) { - double m_to_mm = 1000; + static constexpr const double m_to_mm = 1000; for (ModelObject* obj : this->objects) - if (! only_small_volumes || obj->get_object_stl_stats().volume < 0.001) { // 0.001 = 0.1*0.1*0.1; + if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) { obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm)); - for (ModelVolume* v : obj->volumes) + for (ModelVolume* v : obj->volumes) { + assert(! v->source.is_converted_from_inches); v->source.is_converted_from_meters = true; + } } } @@ -1075,6 +1083,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH; if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER) vol->source.is_converted_from_meters = conv_type == ConversionType::CONV_FROM_METER; + assert(! vol->source.is_converted_from_inches || ! vol->source.is_converted_from_meters); } else vol->set_offset(volume->get_offset()); @@ -1827,6 +1836,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand void ModelVolume::convert_from_imperial_units() { + assert(! this->source.is_converted_from_meters); double in_to_mm = 25.4; this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); this->set_offset(Vec3d(0, 0, 0)); @@ -1835,6 +1845,7 @@ void ModelVolume::convert_from_imperial_units() void ModelVolume::convert_from_meters() { + assert(! this->source.is_converted_from_inches); double m_to_mm = 1000; this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm)); this->set_offset(Vec3d(0, 0, 0)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 665ae80880..4da29afdf4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2388,8 +2388,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ //wxMessageDialog msg_dlg(q, _L( MessageDialog msg_dlg(q, _L( "This file contains several objects positioned at multiple heights.\n" - "Instead of considering them as multiple objects, should I consider\n" - "this file as a single object having multiple parts?") + "\n", + "Instead of considering them as multiple objects, should \n" + "should the file be loaded as a single object having multiple parts?") + "\n", _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { model.convert_multipart_object(nozzle_dmrs->values.size()); @@ -3211,9 +3211,10 @@ void Plater::priv::replace_with_stl() new_volume->set_material_id(old_volume->material_id()); new_volume->set_transformation(old_volume->get_transformation()); new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); + assert(! old_volume->source.is_converted_from_inches || ! old_volume->source.is_converted_from_meters); if (old_volume->source.is_converted_from_inches) new_volume->convert_from_imperial_units(); - if (old_volume->source.is_converted_from_meters) + else if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); new_volume->supported_facets.assign(old_volume->supported_facets); new_volume->seam_facets.assign(old_volume->seam_facets); @@ -3419,9 +3420,10 @@ void Plater::priv::reload_from_disk() new_volume->set_material_id(old_volume->material_id()); new_volume->set_transformation(old_volume->get_transformation()); new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); + assert(! old_volume->source.is_converted_from_inches || ! old_volume->source.is_converted_from_meters); if (old_volume->source.is_converted_from_inches) new_volume->convert_from_imperial_units(); - if (old_volume->source.is_converted_from_meters) + else if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); From 54897aeac0ca70afa9c0e002795d29eb9af8e9bd Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 5 Aug 2021 17:33:41 +0200 Subject: [PATCH 031/240] Fixed previous commit --- src/libslic3r/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index c45acc0484..5383581778 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -476,7 +476,7 @@ bool Model::looks_like_imperial_units() const void Model::convert_from_imperial_units(bool only_small_volumes) { - static constexpr const in_to_mm = 25.4; + static constexpr const double in_to_mm = 25.4; for (ModelObject* obj : this->objects) if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) { obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); From deda13ffc7408772e541dc9514e51e57204949d5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 5 Aug 2021 17:33:41 +0200 Subject: [PATCH 032/240] Fixed previous commit --- src/libslic3r/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index c45acc0484..5383581778 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -476,7 +476,7 @@ bool Model::looks_like_imperial_units() const void Model::convert_from_imperial_units(bool only_small_volumes) { - static constexpr const in_to_mm = 25.4; + static constexpr const double in_to_mm = 25.4; for (ModelObject* obj : this->objects) if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) { obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm)); From c09e7fb2e2f88d1eb063d06112fa64ce05fec8cd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 6 Aug 2021 11:37:02 +0200 Subject: [PATCH 033/240] MSW-specific: Notebook control: Fixed layout of notebook tabs --- src/slic3r/GUI/Notebook.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp index bcc1d2e595..b328fc583f 100644 --- a/src/slic3r/GUI/Notebook.cpp +++ b/src/slic3r/GUI/Notebook.cpp @@ -24,7 +24,7 @@ ButtonsListCtrl::ButtonsListCtrl(wxWindow *parent, bool add_mode_buttons/* = fal m_sizer = new wxBoxSizer(wxHORIZONTAL); this->SetSizer(m_sizer); - m_buttons_sizer = new wxFlexGridSizer(4, m_btn_margin, m_btn_margin); + m_buttons_sizer = new wxFlexGridSizer(1, m_btn_margin, m_btn_margin); m_sizer->Add(m_buttons_sizer, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxBOTTOM, m_btn_margin); if (add_mode_buttons) { @@ -111,6 +111,7 @@ bool ButtonsListCtrl::InsertPage(size_t n, const wxString& text, bool bSelect/* Slic3r::GUI::wxGetApp().UpdateDarkUI(btn); m_pageButtons.insert(m_pageButtons.begin() + n, btn); m_buttons_sizer->Insert(n, new wxSizerItem(btn)); + m_buttons_sizer->SetCols(m_buttons_sizer->GetCols() + 1); m_sizer->Layout(); return true; } From 01f32e18d6e5fc520a947d3b1b59cb41bf6d9c20 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Aug 2021 13:03:30 +0200 Subject: [PATCH 034/240] Fixed build on Linux, abs->std::abs --- src/libslic3r/QuadricEdgeCollapse.cpp | 7 ++++--- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index e42ed5deb2..a66dbc192a 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -2,6 +2,7 @@ #include #include "MutablePriorityQueue.hpp" #include "SimplifyMeshImpl.hpp" +#include using namespace Slic3r; @@ -316,7 +317,7 @@ double QuadricEdgeCollapse::calculate_error(uint32_t id_v1, const Vertices &vertices) { double det = calculate_determinant(q); - if (abs(det) < std::numeric_limits::epsilon()) { + if (std::abs(det) < std::numeric_limits::epsilon()) { // can't divide by zero auto verts = create_vertices(id_v1, id_v2, vertices); auto errors = vertices_error(q, verts); @@ -333,7 +334,7 @@ Vec3f QuadricEdgeCollapse::calculate_vertex(uint32_t id_v1, const Vertices &vertices) { double det = calculate_determinant(q); - if (abs(det) < std::numeric_limits::epsilon()) { + if (std::abs(det) < std::numeric_limits::epsilon()) { // can't divide by zero auto verts = create_vertices(id_v1, id_v2, vertices); auto errors = vertices_error(q, verts); @@ -650,4 +651,4 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, its.indices[ti_new++] = its.indices[ti]; } its.indices.erase(its.indices.begin() + ti_new, its.indices.end()); -} \ No newline at end of file +} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 92654c6064..694eeadd4e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -9,6 +9,9 @@ #include "libslic3r/Model.hpp" #include "libslic3r/QuadricEdgeCollapse.hpp" +#include +#include + namespace Slic3r::GUI { GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, @@ -274,8 +277,8 @@ void GLGizmoSimplify::process() state = State::settings; } // need to render last status fn - // without Sleep it freeze until mouse move - Sleep(50); + // without sleep it freezes until mouse move + std::this_thread::sleep_for(std::chrono::milliseconds(50)); m_parent.schedule_extra_frame(0); }); } From d543d2f29de9f6deecbb444eb6812f27bdc868d3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 6 Aug 2021 15:04:23 +0200 Subject: [PATCH 035/240] Fixed "volume is splittable" detection --- src/libslic3r/MeshSplitImpl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp index 7946411aa9..7233ac40d6 100644 --- a/src/libslic3r/MeshSplitImpl.hpp +++ b/src/libslic3r/MeshSplitImpl.hpp @@ -157,6 +157,7 @@ template bool its_is_splittable(const Its &m) const auto& neighbor_index = ItsWithNeighborsIndex_::get_index(m); std::vector visited(its.indices.size(), false); + its_find_unvisited_neighbors(its, neighbor_index, visited); auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited); return !faces.empty(); From b1330580b33d03b9717479b0271f6588f056bf8a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 6 Aug 2021 15:08:11 +0200 Subject: [PATCH 036/240] MessageDialog: Set html page size respecting to text size + Fixed layout of the unit in "Height ranges" panel --- src/slic3r/GUI/GUI_ObjectLayers.cpp | 2 +- src/slic3r/GUI/MsgDialog.cpp | 35 ++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index c243d88cab..b7ff8e48f4 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -145,7 +145,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm")); temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->SetFont(wxGetApp().normal_font()); - sizer->Add(temp, 0, wxLEFT, wxGetApp().em_unit()); + sizer->Add(temp, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); m_grid_sizer->Add(sizer); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 9af642a250..4e80052426 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -61,7 +61,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); topsizer->Add(logo, 0, wxALL, BORDER); - topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER); + topsizer->Add(rightsizer, 1, wxTOP | wxBOTTOM | wxRIGHT | wxEXPAND, BORDER); SetSizerAndFit(topsizer); } @@ -98,7 +98,6 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin msg_lines++; } - html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), monospaced_font ? 30 * wxGetApp().em_unit() : 2 * msg_lines * wxGetApp().em_unit())); wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont monospace = wxGetApp().code_font(); wxColour text_clr = wxGetApp().get_label_clr_default(); @@ -109,6 +108,30 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size }; html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size); html->SetBorders(2); + + // calculate html page size from text + wxSize page_size; + int em = wxGetApp().em_unit(); + + // if message containes the table + if (msg.Contains("")) { + int lines = msg.Freq('\n') + 1; + int pos = 0; + while (pos < (int)msg.Len() && pos != wxNOT_FOUND) { + pos = msg.find("", pos + 1); + lines += 2; + } + int page_height = std::min(int(font.GetPixelSize().y+2) * lines, 68 * em); + page_size = wxSize(68 * em, page_height); + } + else { + wxClientDC dc(parent); + wxSize msg_sz = dc.GetMultiLineTextExtent(msg); + page_size = wxSize(std::min(msg_sz.GetX() + em, 68 * em), + std::min(msg_sz.GetY() + 2 * em, 68 * em)); + } + html->SetMinSize(page_size); + std::string msg_escaped = xml_escape(msg.ToUTF8().data()); boost::replace_all(msg_escaped, "\r\n", "
"); boost::replace_all(msg_escaped, "\n", "
"); @@ -116,7 +139,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin // Code formatting will be preserved. This is useful for reporting errors from the placeholder parser. msg_escaped = std::string("
") + msg_escaped + "
"; html->SetPage("" + wxString::FromUTF8(msg_escaped.data()) + ""); - content_sizer->Add(html, 1, wxEXPAND | wxBOTTOM, 30); + content_sizer->Add(html, 1, wxEXPAND); } // ErrorDialog @@ -131,7 +154,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_ add_btn(wxID_OK, true); // Use a small bitmap with monospaced font, as the error text will not be wrapped. - logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/92)); + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/84)); wxGetApp().UpdateDlgDarkUI(this); @@ -155,7 +178,7 @@ WarningDialog::WarningDialog(wxWindow *parent, if (style & wxYES) add_btn(wxID_YES); if (style & wxNO) add_btn(wxID_NO); - logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 90)); + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 84)); wxGetApp().UpdateDlgDarkUI(this); Fit(); @@ -180,7 +203,7 @@ MessageDialog::MessageDialog(wxWindow* parent, logo->SetBitmap(create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" : style & wxICON_INFORMATION ? "info.png" : - style & wxICON_QUESTION ? "question" : "PrusaSlicer_192px_grayscale.png", this, 90)); + style & wxICON_QUESTION ? "question" : "PrusaSlicer_192px_grayscale.png", this, 84)); wxGetApp().UpdateDlgDarkUI(this); Fit(); From ef5c94f90adec4f77ec56e3dd5f32b564dde8bc7 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 6 Aug 2021 15:11:20 +0200 Subject: [PATCH 037/240] Fix: prevent degeneration of model during simplification --- src/libslic3r/QuadricEdgeCollapse.cpp | 27 ++++++++++- tests/data/simplification.obj | 46 +++++++++++++++++++ tests/libslic3r/test_indexed_triangle_set.cpp | 12 ++++- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/data/simplification.obj diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index e42ed5deb2..ffe1e5ca78 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -77,7 +77,8 @@ namespace QuadricEdgeCollapse { uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); - + bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info, + const EdgeInfos &e_infos, const Indices &indices); // find edge with smallest error in triangle Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos); Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index); @@ -179,6 +180,8 @@ void Slic3r::its_quadric_edge_collapse( find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; if (!ti1_opt.has_value() || // edge has only one triangle + degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) || + degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) || is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) || is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) { // try other triangle's edge @@ -487,6 +490,28 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, return false; } +bool QuadricEdgeCollapse::degenerate(uint32_t vi, + uint32_t ti0, + uint32_t ti1, + const VertexInfo &v_info, + const EdgeInfos & e_infos, + const Indices & indices) +{ + // check surround triangle do not contain vertex index + // protect from creation of triangle with two same vertices inside + size_t v_info_end = v_info.start + v_info.count; + for (size_t ei = v_info.start; ei < v_info_end; ++ei) { + assert(ei < e_infos.size()); + const EdgeInfo &e_info = e_infos[ei]; + if (e_info.t_index == ti0) continue; // ti0 will be deleted + if (e_info.t_index == ti1) continue; // ti1 will be deleted + const Triangle &t = indices[e_info.t_index]; + for (size_t i = 0; i < 3; ++i) + if (t[i] == vi) return true; + } + return false; +} + Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t, const Vertices & vertices, const VertexInfos &v_infos) diff --git a/tests/data/simplification.obj b/tests/data/simplification.obj new file mode 100644 index 0000000000..f509e695ca --- /dev/null +++ b/tests/data/simplification.obj @@ -0,0 +1,46 @@ +v 39.349007 -54.069000 -199.819000 +v 39.489006 -54.029007 -199.815002 +v 39.419006 -53.993011 -199.769012 +v 39.629005 -53.975006 -199.815002 +v 39.639008 -53.947006 -199.805023 +v 39.651001 -53.919006 -199.795013 +v 39.807007 -53.863007 -199.796997 +v 39.729004 -53.891006 -199.796997 +v 39.727005 -53.935013 -199.813019 +v 39.767006 -53.899002 -199.805023 +v 39.871002 -53.835007 -199.801025 +v 39.443001 -53.829010 -199.878998 +v 39.523003 -53.965012 -199.827026 +v 39.807007 -53.863007 -199.796997 +v 39.833008 -53.723007 -199.723022 +v 39.759003 -53.822998 -199.822998 +v 39.867004 -53.845001 -199.805023 +v 39.937004 -53.805008 -199.805023 +f 1 2 3 +f 4 5 2 +f 2 6 3 +f 7 8 4 +f 9 10 4 +f 10 9 11 +f 12 2 1 +f 13 6 4 +f 13 4 2 +f 8 7 9 +f 6 9 4 +f 6 14 15 +f 16 14 6 +f 17 18 9 +f 3 6 15 +f 12 16 6 +f 12 6 13 +f 12 13 2 +f 5 4 8 +f 6 8 9 +f 5 6 2 +f 6 5 8 +f 17 9 7 +f 7 11 17 +f 18 11 9 +f 11 18 17 +f 10 7 4 +f 7 10 11 diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index b640d8410c..0e08749bf0 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -246,4 +246,14 @@ TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") CHECK(fabs(original_volume - volume) < 33.); float avg_distance = compare(mesh.its, its, 10); CHECK(avg_distance < 0.022f); // 0.02022 | 0.0199614074 -} \ No newline at end of file +} + +TEST_CASE("Simplify trouble case", "[its]") +{ + TriangleMesh tm = load_model("simplification.obj"); + REQUIRE_FALSE(tm.empty()); + float max_error = std::numeric_limits::max(); + uint32_t wanted_count = 8; + its_quadric_edge_collapse(tm.its, wanted_count, &max_error); + CHECK(tm.its.indices.size() <= 8); +} From 85c7dea1a98530a09c4485fbbf582dd6012f6cf3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Aug 2021 16:02:32 +0200 Subject: [PATCH 038/240] Finished concept of gizmos with no toolbar icon: on_is_selectable and on_is_activable functions are now completely independent, the former says if there shall be an icon in the left toolbar, the latter says if the gizmo can be activated (by a shortcut or GLGizmoManager::open_gizmo) --- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 5 ++++ .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 27 ++++++++----------- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 - 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index f6e7708fae..5bf2c1556a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -51,6 +51,11 @@ bool GLGizmoMmuSegmentation::on_is_selectable() const && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_edited_cnt() > 1); } +bool GLGizmoMmuSegmentation::on_is_activable() const +{ + return GLGizmoPainterBase::on_is_activable() && wxGetApp().extruders_edited_cnt() > 1; +} + static std::vector> get_extruders_colors() { unsigned char rgb_color[3] = {}; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index e890ca0312..b1b19bfaca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -109,6 +109,7 @@ protected: std::string on_get_name() const override; bool on_is_selectable() const override; + bool on_is_activable() const override; wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 317d7ebcaa..507aeb021b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -515,7 +515,7 @@ bool GLGizmoPainterBase::on_is_activable() const const Selection& selection = m_parent.get_selection(); if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF - || !selection.is_single_full_instance()) + || !selection.is_single_full_instance() || wxGetApp().get_mode() == comSimple) return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 49c3e8dbda..bd36373607 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -40,7 +40,7 @@ protected: virtual void on_render_for_picking() override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual bool on_is_activable() const override; - virtual bool on_is_selectable() const override { return false; }; + virtual bool on_is_selectable() const override { return false; } private: void close(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 8871fec23e..eda95a2a55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -51,14 +51,7 @@ std::vector GLGizmosManager::get_selectable_idxs() const return out; } -std::vector GLGizmosManager::get_activable_idxs() const -{ - std::vector out; - for (size_t i=0; iis_activable()) - out.push_back(i); - return out; -} + size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const { @@ -171,7 +164,7 @@ void GLGizmosManager::refresh_on_off_state() return; if (m_current != Undefined - && (! m_gizmos[m_current]->is_activable() || ! m_gizmos[m_current]->is_selectable())) { + && ! m_gizmos[m_current]->is_activable()) { activate_gizmo(Undefined); update_data(); } @@ -189,7 +182,7 @@ void GLGizmosManager::reset_all_states() bool GLGizmosManager::open_gizmo(EType type) { int idx = int(type); - if (/*m_gizmos[idx]->is_selectable() &&*/ m_gizmos[idx]->is_activable()) { + if (m_gizmos[idx]->is_activable()) { activate_gizmo(m_current == idx ? Undefined : (EType)idx); update_data(); return true; @@ -306,7 +299,7 @@ bool GLGizmosManager::handle_shortcut(int key) auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(), [key](const std::unique_ptr& gizmo) { int gizmo_key = gizmo->get_shortcut_key(); - return gizmo->is_selectable() + return gizmo->is_activable() && ((gizmo_key == key - 64) || (gizmo_key == key - 96)); }); @@ -1079,8 +1072,7 @@ void GLGizmosManager::do_render_overlay() const float u_offset = 1.0f / (float)tex_width; float v_offset = 1.0f / (float)tex_height; - float toolbar_top = 0.f; - float current_y = 0.f; + float current_y = FLT_MAX; for (size_t idx : selectable_idxs) { GLGizmoBase* gizmo = m_gizmos[idx].get(); @@ -1094,15 +1086,18 @@ void GLGizmosManager::do_render_overlay() const float u_right = u_left + du - u_offset; GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); - if (idx == m_current) { - toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); + if (idx == m_current || current_y == FLT_MAX) { + // The FLT_MAX trick is here so that even non-selectable but activable + // gizmos are passed some meaningful value. current_y = 0.5f * cnv_h - zoomed_top_y * zoom; } zoomed_top_y -= zoomed_stride_y; } - if (m_current != Undefined) + if (m_current != Undefined) { + float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); m_gizmos[m_current]->render_input_window(width, current_y, toolbar_top); + } } float GLGizmosManager::get_scaled_total_height() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index db628a0b76..cdfb3f6ff7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -102,7 +102,6 @@ private: std::pair m_highlight; // bool true = higlightedShown, false = highlightedHidden std::vector get_selectable_idxs() const; - std::vector get_activable_idxs() const; size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; void activate_gizmo(EType type); From 17860fb38a53d6b7c06b28d39bdeb4d57dfdf4e1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Aug 2021 16:30:02 +0200 Subject: [PATCH 039/240] Added cancel button into 'Save support points?' message box, changed the title --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fa61779ec9..15f289251f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -906,10 +906,11 @@ void GLGizmoSlaSupports::on_set_state() // on OSX with the wxMessageDialog being shown several times when clicked into. //wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " - "edited support points?") + "\n",_L("Save changes?"), wxICON_QUESTION | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) + "edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL ); + int ret = dlg.ShowModal(); + if (ret == wxID_YES) editing_mode_apply_changes(); - else + else if (ret == wxID_NO) editing_mode_discard_changes(); }); // refuse to be turned off so the gizmo is active when the CallAfter is executed From cf2a7f4dfac32ced973a862fdeba9ccb4ca9d0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 6 Aug 2021 15:26:26 +0200 Subject: [PATCH 040/240] Fixed the possibility of changing brush size in the multi-material gizmo by ALT+mouse wheel when brush for painting single triangles is selected. --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 317d7ebcaa..6d3d162b73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -283,7 +283,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return true; } else if (alt_down) { - if (m_tool_type == ToolType::BRUSH) { + if (m_tool_type == ToolType::BRUSH && (m_cursor_type == TriangleSelector::CursorType::SPHERE || m_cursor_type == TriangleSelector::CursorType::CIRCLE)) { m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); m_parent.set_as_dirty(); From 666cdeecd988b3da6b5bbe5b2d38df399d98f8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 6 Aug 2021 15:27:54 +0200 Subject: [PATCH 041/240] Forced recomputation of seed fill selected areas when the seed fill angle is changed by ALT+mouse wheel. --- src/libslic3r/TriangleSelector.cpp | 8 ++++---- src/libslic3r/TriangleSelector.hpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 5 +++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 758b3ab4d5..9e16c03178 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -178,12 +178,12 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, } } -void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, float seed_fill_angle) +void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, float seed_fill_angle, bool force_reselection) { assert(facet_start < m_orig_size_indices); // Recompute seed fill only if the cursor is pointing on facet unselected by seed fill. - if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill()) + if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection) return; this->seed_fill_unselect_all_triangles(); @@ -278,7 +278,7 @@ void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi, return; auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx, Partition partition) -> void { - assert(subtriangle_idx == -1); + assert(subtriangle_idx != -1); if (!m_triangles[subtriangle_idx].is_split()) touching_subtriangles_out.emplace_back(subtriangle_idx); else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1) @@ -312,7 +312,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_ } auto get_all_touching_triangles = [this](int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated) -> std::vector { - assert(facet_idx != -1 && facet_idx < m_triangles.size()); + assert(facet_idx != -1 && facet_idx < int(m_triangles.size())); assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors)); std::vector touching_triangles; Vec3i vertices = {m_triangles[facet_idx].verts_idxs[0], m_triangles[facet_idx].verts_idxs[1], m_triangles[facet_idx].verts_idxs[2]}; diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index eeb479dee6..39c1ad22e3 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -49,7 +49,8 @@ public: void seed_fill_select_triangles(const Vec3f &hit, // point where to start int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - float seed_fill_angle); // the maximal angle between two facets to be painted by the same color + float seed_fill_angle, // the maximal angle between two facets to be painted by the same color + bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle void bucket_fill_select_triangles(const Vec3f &hit, // point where to start int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 6d3d162b73..1045ac9e64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -292,6 +292,11 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_seed_fill_angle = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_seed_fill_angle - SeedFillAngleStep, SeedFillAngleMin) : std::min(m_seed_fill_angle + SeedFillAngleStep, SeedFillAngleMax); m_parent.set_as_dirty(); + if (m_rr.mesh_id != -1) { + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle, true); + m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); + m_seed_fill_last_mesh_id = m_rr.mesh_id; + } return true; } From 56d4ba7a296273d51872cdcb3b9fbd808906523e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 6 Aug 2021 15:28:36 +0200 Subject: [PATCH 042/240] Fixed the issue when the object was selected by seed fill or bucket fill could stay selected when the painting tool was switched to brush. --- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index f6e7708fae..41279c9f14 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -322,8 +322,15 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::AlignTextToFramePadding(); ImGui::SameLine(tool_type_offset + m_imgui->scaled(0.f)); ImGui::PushItemWidth(tool_type_radio_brush); - if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) + if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) { m_tool_type = GLGizmoMmuSegmentation::ToolType::BRUSH; + for (auto &triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + } + + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); From dd55b5585309111ac546964b97d0ac3d59b5b221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 6 Aug 2021 15:29:09 +0200 Subject: [PATCH 043/240] Swapped the position of seed fill and bucket fill inside the multi-material gizmo. Changed the default value for the seed fill angle. --- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 36 +++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 41279c9f14..e61fc2b9f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -341,24 +341,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott } ImGui::SameLine(tool_type_offset + tool_type_radio_brush + m_imgui->scaled(0.f)); - ImGui::PushItemWidth(tool_type_radio_bucket_fill); - if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BUCKET_FILL)) { - m_tool_type = GLGizmoMmuSegmentation::ToolType::BUCKET_FILL; - for (auto &triangle_selector : m_triangle_selectors) { - triangle_selector->seed_fill_unselect_all_triangles(); - triangle_selector->request_update_render_data(); - } - } - - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Paints neighboring facets that have the same color.").ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } - - ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_bucket_fill + m_imgui->scaled(0.f)); ImGui::PushItemWidth(tool_type_radio_seed_fill); if (m_imgui->radio_button(m_desc["tool_seed_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::SEED_FILL)) { m_tool_type = GLGizmoMmuSegmentation::ToolType::SEED_FILL; @@ -376,6 +358,24 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::EndTooltip(); } + ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_seed_fill + m_imgui->scaled(0.f)); + ImGui::PushItemWidth(tool_type_radio_bucket_fill); + if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BUCKET_FILL)) { + m_tool_type = GLGizmoMmuSegmentation::ToolType::BUCKET_FILL; + for (auto &triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + } + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Paints neighboring facets that have the same color.").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + ImGui::Separator(); if(m_tool_type == ToolType::BRUSH) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 04c74c4879..4db17d5974 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -114,7 +114,7 @@ protected: bool m_triangle_splitting_enabled = true; ToolType m_tool_type = ToolType::BRUSH; - float m_seed_fill_angle = 0.f; + float m_seed_fill_angle = 30.f; static constexpr float SeedFillAngleMin = 0.0f; static constexpr float SeedFillAngleMax = 90.f; From b174dacc85357af41857e4dc858add3ab2e1b6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Sat, 7 Aug 2021 08:29:32 +0200 Subject: [PATCH 044/240] Added a missing include (GCC 11.1) --- src/libslic3r/QuadricEdgeCollapse.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index a66dbc192a..95c7b40d83 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -1,5 +1,6 @@ #include "QuadricEdgeCollapse.hpp" #include +#include #include "MutablePriorityQueue.hpp" #include "SimplifyMeshImpl.hpp" #include From 13604469fdd9a255f2d0790ee516215e44b7a26e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 7 Aug 2021 08:12:32 +0200 Subject: [PATCH 045/240] Painting gizmos and sinking objects: 1. Do not show what's below the bed when a gizmo is open 2. Triangulate the cut so people don't see inside 3. If regular clipping plane is used, the cuts are mutually clipped by one another 4. Painting itself ignores the hidden part of the object --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 64 ++++++++++++- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 + src/slic3r/GUI/MeshUtils.cpp | 94 ++++++++++++++++---- src/slic3r/GUI/MeshUtils.hpp | 6 ++ 5 files changed, 152 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 68f0f3f993..4f3e31beaa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -67,8 +67,8 @@ void GLGizmoFdmSupports::render_painter_gizmo() const glsafe(::glEnable(GL_DEPTH_TEST)); render_triangles(selection); - m_c->object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); render_cursor(); glsafe(::glDisable(GL_BLEND)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 115a675aca..18ce9d73c4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -150,6 +150,30 @@ void InstancesHider::on_update() canvas->toggle_model_objects_visibility(false); canvas->toggle_model_objects_visibility(true, mo, active_inst); canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + canvas->set_use_clipping_planes(true); + // Some objects may be sinking, do not show whatever is below the bed. + canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), 0.)); + canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), std::numeric_limits::max())); + + + std::vector meshes; + for (const ModelVolume* mv : mo->volumes) + meshes.push_back(&mv->mesh()); + + if (meshes != m_old_meshes) { + m_clippers.clear(); + for (const TriangleMesh* mesh : meshes) { + m_clippers.emplace_back(new MeshClipper); + if (mo->get_instance_min_z(active_inst) < SINKING_Z_THRESHOLD) + m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), 0.)); + else { + m_clippers.back()->set_plane(ClippingPlane::ClipsNothing()); + m_clippers.back()->set_limiting_plane(ClippingPlane::ClipsNothing()); + } + m_clippers.back()->set_mesh(*mesh); + } + m_old_meshes = meshes; + } } else canvas->toggle_model_objects_visibility(true); @@ -158,6 +182,9 @@ void InstancesHider::on_update() void InstancesHider::on_release() { get_pool()->get_canvas()->toggle_model_objects_visibility(true); + get_pool()->get_canvas()->set_use_clipping_planes(false); + m_old_meshes.clear(); + m_clippers.clear(); } void InstancesHider::show_supports(bool show) { @@ -167,6 +194,38 @@ void InstancesHider::show_supports(bool show) { } } +void InstancesHider::render_cut() const +{ + const SelectionInfo* sel_info = get_pool()->selection_info(); + const ModelObject* mo = sel_info->model_object(); + Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + + size_t clipper_id = 0; + for (const ModelVolume* mv : mo->volumes) { + Geometry::Transformation vol_trafo = mv->get_transformation(); + Geometry::Transformation trafo = inst_trafo * vol_trafo; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_transformation(trafo); + const ObjectClipper* obj_clipper = get_pool()->object_clipper(); + if (obj_clipper->is_valid() && obj_clipper->get_clipping_plane() + && obj_clipper->get_position() != 0.) { + ClippingPlane clp = *get_pool()->object_clipper()->get_clipping_plane(); + clp.set_normal(-clp.get_normal()); + clipper->set_limiting_plane(clp); + } else + clipper->set_limiting_plane(ClippingPlane::ClipsNothing()); + + glsafe(::glPushMatrix()); + glsafe(::glColor3f(0.8f, 0.3f, 0.0f)); + clipper->render_cut(); + glsafe(::glPopMatrix()); + + ++clipper_id; + } +} + void HollowedMesh::on_update() @@ -348,6 +407,7 @@ void ObjectClipper::render_cut() const const SelectionInfo* sel_info = get_pool()->selection_info(); const ModelObject* mo = sel_info->model_object(); Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + const bool sinking = mo->bounding_box().min.z() < SINKING_Z_THRESHOLD; size_t clipper_id = 0; for (const ModelVolume* mv : mo->volumes) { @@ -358,7 +418,9 @@ void ObjectClipper::render_cut() const auto& clipper = m_clippers[clipper_id]; clipper->set_plane(*m_clp); clipper->set_transformation(trafo); - + clipper->set_limiting_plane(sinking ? + ClippingPlane(Vec3d::UnitZ(), 0.) + : ClippingPlane::ClipsNothing()); glsafe(::glPushMatrix()); glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); clipper->render_cut(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 6f59dc95ed..228f5b58c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -180,6 +180,7 @@ public: void show_supports(bool show); bool are_supports_shown() const { return m_show_supports; } + void render_cut() const; protected: void on_update() override; @@ -187,6 +188,8 @@ protected: private: bool m_show_supports = false; + std::vector m_old_meshes; + std::vector> m_clippers; }; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 073bb6f78d..d889bc538b 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -24,6 +24,15 @@ void MeshClipper::set_plane(const ClippingPlane& plane) } +void MeshClipper::set_limiting_plane(const ClippingPlane& plane) +{ + if (m_limiting_plane != plane) { + m_limiting_plane = plane; + m_triangles_valid = false; + } +} + + void MeshClipper::set_mesh(const TriangleMesh& mesh) { @@ -89,18 +98,71 @@ void MeshClipper::recalculate_triangles() std::vector neg_polys = slice_mesh_ex(m_negative_mesh->its, std::vector{height_mesh}, slicing_params); list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front()); } - - m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); - // Rotate the cut into world coords: + // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), up); Transform3d tr = Transform3d::Identity(); tr.rotate(q); tr = m_trafo.get_matrix() * tr; + height_mesh += 0.001f; // to avoid z-fighting - // to avoid z-fighting - height_mesh += 0.001f; + if (m_limiting_plane != ClippingPlane::ClipsNothing()) + { + // Now remove whatever ended up below the limiting plane (e.g. sinking objects). + // First transform the limiting plane from world to mesh coords. + // Note that inverse of tr transforms the plane from world to horizontal. + Vec3d normal_old = m_limiting_plane.get_normal().normalized(); + Vec3d normal_new = (tr.matrix().block<3,3>(0,0).transpose() * normal_old).normalized(); + + // normal_new should now be the plane normal in mesh coords. To find the offset, + // transform a point and set offset so it belongs to the transformed plane. + Vec3d pt = Vec3d::Zero(); + double plane_offset = m_limiting_plane.get_data()[3]; + if (std::abs(normal_old.z()) > 0.5) // normal is normalized, at least one of the coords if larger than sqrt(3)/3 = 0.57 + pt.z() = - plane_offset / normal_old.z(); + else if (std::abs(normal_old.y()) > 0.5) + pt.y() = - plane_offset / normal_old.y(); + else + pt.x() = - plane_offset / normal_old.x(); + pt = tr.inverse() * pt; + double offset = -(normal_new.dot(pt)); + + if (std::abs(normal_old.dot(m_plane.get_normal().normalized())) > 0.99) { + // The cuts are parallel, show all or nothing. + if (offset < height_mesh) + list_of_expolys.front().clear(); + } else { + // The cut is a horizontal plane defined by z=height_mesh. + // ax+by+e=0 is the line of intersection with the limiting plane. + // Normalized so a^2 + b^2 = 1. + double len = std::hypot(normal_new.x(), normal_new.y()); + if (len == 0.) + return; + double a = normal_new.x() / len; + double b = normal_new.y() / len; + double e = (normal_new.z() * height_mesh + offset) / len; + if (b == 0.) + return; + + // We need a half-plane to limit the cut. Get angle of the intersecting line. + double angle = std::atan(-a/b); + if (b > 0) // select correct half-plane + angle += M_PI; + + // We'll take a big rectangle above x-axis and rotate and translate + // it so it lies on our line. This will be the figure to subtract + // from the cut. The coordinates must not overflow after the transform, + // make the rectangle a bit smaller. + coord_t size = (std::numeric_limits::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; + ExPolygons ep {ExPolygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; + ep.front().rotate(angle); + ep.front().translate(scale_(-e * a), scale_(-e * b)); + list_of_expolys.front() = diff_ex(list_of_expolys.front(), ep.front()); + } + } + + m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); m_vertex_array.release_geometry(); for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { @@ -159,17 +221,19 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& unsigned i = 0; - // Remove points that are obscured or cut by the clipping plane - if (clipping_plane) { - for (i=0; iis_point_clipped(trafo * hits[i].position())) - break; + // Remove points that are obscured or cut by the clipping plane. + // Also, remove anything below the bed (sinking objects). + for (i=0; i= 0. && + (! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit))) + break; + } - if (i==hits.size() || (hits.size()-i) % 2 != 0) { - // All hits are either clipped, or there is an odd number of unclipped - // hits - meaning the nearest must be from inside the mesh. - return false; - } + if (i==hits.size() || (hits.size()-i) % 2 != 0) { + // All hits are either clipped, or there is an odd number of unclipped + // hits - meaning the nearest must be from inside the mesh. + return false; } // Now stuff the points in the provided vector and calculate normals if asked about them: diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 07b01e27f6..ec6c337c06 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -71,6 +71,11 @@ public: // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); + // In case the object is clipped by two planes (e.g. in case of sinking + // objects), this will be used to clip the triagnulated cut. + // Pass ClippingPlane::ClipsNothing to turn this off. + void set_limiting_plane(const ClippingPlane& plane); + // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. void set_mesh(const TriangleMesh& mesh); @@ -92,6 +97,7 @@ private: const TriangleMesh* m_mesh = nullptr; const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; + ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array; bool m_triangles_valid = false; From ef30cc8690b33a05a77c2d65b4ebd42f0eee353f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 9 Aug 2021 11:49:48 +0200 Subject: [PATCH 046/240] Fixup of previous commit: there are two more painting gizmos --- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index ec48c511f6..b452b2f4bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -147,6 +147,7 @@ void GLGizmoMmuSegmentation::render_painter_gizmo() const render_triangles(selection, false); m_c->object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); render_cursor(); glsafe(::glDisable(GL_BLEND)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 8854c1a7e2..ff9888e199 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -63,6 +63,7 @@ void GLGizmoSeam::render_painter_gizmo() const render_triangles(selection); m_c->object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); render_cursor(); glsafe(::glDisable(GL_BLEND)); From 1f131d130f3172341e7ae566f1955f8d6d6dbc64 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 Aug 2021 10:45:05 +0200 Subject: [PATCH 047/240] DoubleSlider with MM-printer preset: When Object has "Paint-on segmentation", DoubleSlider has same behavior as with modifiers + MSW specific: Use BitmapComboBox instead of wxBitmapComboBox to DarkMode for extruder selectors --- src/slic3r/GUI/DoubleSlider.cpp | 3 ++- src/slic3r/GUI/ExtraRenderers.cpp | 5 +++++ src/slic3r/GUI/GUI_ObjectList.cpp | 5 +++++ src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/ObjectDataViewModel.cpp | 12 +++++++++++- src/slic3r/GUI/ObjectDataViewModel.hpp | 1 + 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index fc93262a2e..9134d6988d 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -11,6 +11,7 @@ #include "GUI_Utils.hpp" #include "MsgDialog.hpp" #include "Tab.hpp" +#include "GUI_ObjectList.hpp" #include #include @@ -1588,7 +1589,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current append_submenu(menu, change_extruder_menu, wxID_ANY, change_extruder_menu_name, _L("Use another extruder"), active_extruders[1] > 0 ? "edit_uni" : "change_extruder", - [this]() {return m_mode == MultiAsSingle; }, GUI::wxGetApp().plater()); + [this]() {return m_mode == MultiAsSingle && !GUI::wxGetApp().obj_list()->has_paint_on_segmentation(); }, GUI::wxGetApp().plater()); } } diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 3fd6b9f04f..277f225afb 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -2,6 +2,7 @@ #include "wxExtensions.hpp" #include "GUI.hpp" #include "I18N.hpp" +#include "BitmapComboBox.hpp" #include #ifdef wxHAS_GENERIC_DATAVIEWCTRL @@ -296,7 +297,11 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR DataViewBitmapText data; data << value; +#ifdef _WIN32 + Slic3r::GUI::BitmapComboBox* c_editor = new Slic3r::GUI::BitmapComboBox(parent, wxID_ANY, wxEmptyString, +#else auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, +#endif labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), 0, nullptr , wxCB_READONLY); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 0eb535ee83..9bbf82039d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4248,5 +4248,10 @@ ModelObject* ObjectList::object(const int obj_idx) const return (*m_objects)[obj_idx]; } +bool ObjectList::has_paint_on_segmentation() +{ + return m_objects_model->HasInfoItem(InfoItemType::MmuSegmentation); +} + } //namespace GUI } //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 0f1bcd6014..b41628a258 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -379,6 +379,7 @@ public: void set_extruder_for_selected_items(const int extruder) const ; wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection = nullptr); void apply_volumes_order(); + bool has_paint_on_segmentation(); private: #ifdef __WXOSX__ diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 8f65e4f902..c7208984e5 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1494,7 +1494,17 @@ void ObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataVie } } -ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const +bool ObjectDataViewModel::HasInfoItem(InfoItemType type) const +{ + for (ObjectDataViewModelNode* obj_node : m_objects) + for (size_t j = 0; j < obj_node->GetChildCount(); j++) + if (obj_node->GetNthChild(j)->GetInfoItemType() == type) + return true; + + return false; +} + +ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const { if (!item.IsOk()) return itUndef; diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 664cf7ff57..b1330f93d6 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -345,6 +345,7 @@ public: // Is the container just a header or an item with all columns // In our case it is an item with all columns bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } + bool HasInfoItem(InfoItemType type) const; ItemType GetItemType(const wxDataViewItem &item) const; InfoItemType GetInfoItemType(const wxDataViewItem &item) const; From 4efce682e1efd96a08f086d6101b3972f940f4d1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 Aug 2021 13:34:00 +0200 Subject: [PATCH 048/240] Fix for #6782 - Clicking On Another Modifier while Changing a Modifier Parameter crashes PrusaSlicer --- src/slic3r/GUI/Plater.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 652524c5cc..2abf9a730e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3918,6 +3918,10 @@ void Plater::priv::on_right_click(RBtnEvent& evt) if (evt.data.second) return; + // Each context menu respects to the selected item in ObjectList, + // so this selection should be updated before menu creation + wxGetApp().obj_list()->update_selections(); + if (printer_technology == ptSLA) menu = menus.sla_object_menu(); else { From b43f225ac8ee7932f4d71a69157c412578885dbd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 9 Aug 2021 17:09:09 +0200 Subject: [PATCH 049/240] Hint notification fixes and improvements Hint notification bold (diff color) headline on multiple lines Hint notification: Documentation link button. Hints.ini header hints logo - clippy disabled a preffered modes and techs for hints notifications Empty hints array check. Fix of #6774. --- resources/data/hints.ini | 69 +++++- resources/icons/notification_clippy.svg | 49 +++++ .../icons/notification_documentation.svg | 86 ++++++++ .../notification_documentation_hover.svg | 97 +++++++++ src/imgui/imconfig.h | 52 ++--- src/slic3r/GUI/HintNotification.cpp | 200 ++++++++++++++---- src/slic3r/GUI/HintNotification.hpp | 21 +- src/slic3r/GUI/ImGuiWrapper.cpp | 56 +++-- 8 files changed, 538 insertions(+), 92 deletions(-) create mode 100644 resources/icons/notification_clippy.svg create mode 100644 resources/icons/notification_documentation.svg create mode 100644 resources/icons/notification_documentation_hover.svg diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 2ebf9091fe..81817f3ca4 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -1,3 +1,54 @@ +# THIS DOCUMENT CONTAINS DATA FOR HINTS NOTIFICATIONS +# +# Each notification is divided by +# [hint:*name of notification*] +# +# Each notification MUST have text var in format: +# text = Headline of hint\nBody of hint. +# Headline is divided by new line (\n) from body. +# Headline is automaticaly printed as Bold. +# Body can contain bold marks: text to be bold (currently rendered as diffenert color, not bold due to font limitations) +# Body can contain hypertext: hypertext text +# Hypertext must be max one per notification and must be closed by +# +# Notification can have documentation link: +# documentation_link = https://help.prusa3d.com/en/article/name-of-article +# +# If notification contains hypertext, it needs to be specified by hypertext_type var. +# each type needs to be supported with one or more additional vars. +# These types are possible: +# +# Settings highlight (like search feature) +# hypertext_type = settings +# hypertext_settings_opt = name_of_settings (hover over settings value and copy last line of hover text) +# hypertext_settings_type = 1 (1 - 5 according to settings tab - to be channged to name of tabs instead of numbers) +# hypertext_settings_category = Infill (name of panel - written on left in settings) +# +# Plater top toolbar highlight +# hypertext_type = plater +# hypertext_plater_item = nameofbutton (internal name of GLToolbar items) +# +# Plater gizmos (left) toolbar highlight +# hypertext_type = gizmo +# hypertext_gizmo_item = name (name of svg icon of gizmo in resources without .svg suffix) +# +# Open preferences (might add item to highlight) +# hypertext_type = preferences +# hypertext_preferences_page = 0 (values 0-2 according to prefernces tab to be opened) +# +# Open gallery (no aditional var) +# hypertext_type = gallery +# +# +# Each notification can have disabled and preferred modes and techs - divided by ; +# preferred_mode = simple +# disabled_mode = advanced; expert +# preferred_tech = SLA +# disabled_tech = FFF; MMU +# Algorithm shows hint only if in preffered mode / tech. +# Algorithm shows hint only if not in disabled mode / tech. +# if there are both disabled and preferred, only preferred that are not in disabled are valid. + [hint:Perspective camera] text = Perspective camera\nDid you know that you can use the K key to quickly switch between an orthographic and perspective camera? @@ -45,12 +96,12 @@ hypertext_plater_item = arrange [hint:Reload from disk] text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 +documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 [hint:Different layer height for each model] text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/per-model-settings_1674 +documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 [hint:Solid infill threshold area] text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set theSolid infill threshold area.(Expert mode only.) @@ -91,7 +142,7 @@ text = Load config from G-code\nDid you know that you can use File-Import Config [hint:Ironing] text = Ironing\nDid you know that you can smooth top surfaces of prints using Ironing? The nozzle will run a special second infill phase at the same layer to fill in holes and flatten any lifted plastic. Read more in thedocumentation. (Requires Advanced or Expert mode.) hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/ironing_177488 +documentation_link = https://help.prusa3d.com/en/article/ironing_177488 disabled_modes = SLA; simple [hint:Fuzzy skin] @@ -105,7 +156,7 @@ disabled_modes = SLA [hint:Negative volume] text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in thedocumentation.(Requires Advanced or Expert mode.) hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/negative-volume_238503 +documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 disabled_modes = SLA; simple [hint:Paint-on supports] @@ -123,17 +174,17 @@ disabled_modes = SLA; simple [hint:Insert Pause] text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer +documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer [hint:Insert Custom G-code] text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer +documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer [hint:Configuration snapshots] text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - Configuration snapshots menu. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 +documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 [hint:Minimum wall thickness] text = Minimum wall thickness\nDid you know that instead of the number of top and bottom layers, you can define theMinimum shell thicknessin millimeters? This feature is especially useful when using the variable layer height function. @@ -151,7 +202,7 @@ hypertext_preferences_page = 2 [hint:Adaptive infills] text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/infill-patterns_177130 +documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 [hint:Fullscreen mode] text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. @@ -159,7 +210,7 @@ text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscre [hint:Simplify mesh] text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 +documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 #[hint:] #text = diff --git a/resources/icons/notification_clippy.svg b/resources/icons/notification_clippy.svg new file mode 100644 index 0000000000..e7eb207fe7 --- /dev/null +++ b/resources/icons/notification_clippy.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/notification_documentation.svg b/resources/icons/notification_documentation.svg new file mode 100644 index 0000000000..21e7cfcb3c --- /dev/null +++ b/resources/icons/notification_documentation.svg @@ -0,0 +1,86 @@ + +image/svg+xml + + + + + + + + + + + + diff --git a/resources/icons/notification_documentation_hover.svg b/resources/icons/notification_documentation_hover.svg new file mode 100644 index 0000000000..0c6b2e2071 --- /dev/null +++ b/resources/icons/notification_documentation_hover.svg @@ -0,0 +1,97 @@ + +image/svg+xml + + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index dfb314d3cf..713499a1ba 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -124,32 +124,34 @@ namespace ImGui const char ColorMarkerEnd = 0x3; // ETX // Special ASCII characters are used here as an ikons markers - const char PrintIconMarker = 0x4; - const char PrinterIconMarker = 0x5; - const char PrinterSlaIconMarker = 0x6; - const char FilamentIconMarker = 0x7; - const char MaterialIconMarker = 0x8; - const char CloseNotifButton = 0xB; - const char CloseNotifHoverButton = 0xC; - const char MinimalizeButton = 0xE; - const char MinimalizeHoverButton = 0xF; - const char WarningMarker = 0x10; - const char ErrorMarker = 0x11; - const char EjectButton = 0x12; - const char EjectHoverButton = 0x13; - const char CancelButton = 0x14; - const char CancelHoverButton = 0x15; - const char VarLayerHeightMarker = 0x16; + const wchar_t PrintIconMarker = 0x4; + const wchar_t PrinterIconMarker = 0x5; + const wchar_t PrinterSlaIconMarker = 0x6; + const wchar_t FilamentIconMarker = 0x7; + const wchar_t MaterialIconMarker = 0x8; + const wchar_t CloseNotifButton = 0xB; + const wchar_t CloseNotifHoverButton = 0xC; + const wchar_t MinimalizeButton = 0xE; + const wchar_t MinimalizeHoverButton = 0xF; + const wchar_t WarningMarker = 0x10; + const wchar_t ErrorMarker = 0x11; + const wchar_t EjectButton = 0x12; + const wchar_t EjectHoverButton = 0x13; + const wchar_t CancelButton = 0x14; + const wchar_t CancelHoverButton = 0x15; + const wchar_t VarLayerHeightMarker = 0x16; - const char RightArrowButton = 0x18; - const char RightArrowHoverButton = 0x19; - const char PreferencesButton = 0x1A; - const char PreferencesHoverButton = 0x1B; - const char SinkingObjectMarker = 0x1C; - const char CustomSupportsMarker = 0x1D; - const char CustomSeamMarker = 0x1E; - const char MmuSegmentationMarker = 0x1F; - + const wchar_t RightArrowButton = 0x18; + const wchar_t RightArrowHoverButton = 0x19; + const wchar_t PreferencesButton = 0x1A; + const wchar_t PreferencesHoverButton = 0x1B; + const wchar_t SinkingObjectMarker = 0x1C; + const wchar_t CustomSupportsMarker = 0x1D; + const wchar_t CustomSeamMarker = 0x1E; + const wchar_t MmuSegmentationMarker = 0x1F; + const wchar_t DocumentationButton = 0x2600; + const wchar_t DocumentationHoverButton = 0x2601; + const wchar_t ClippyMarker = 0x2602; // void MyFunction(const char* name, const MyMatrix44& v); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index fe4f3c9abe..a4ef5b963a 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -31,9 +31,9 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f ImGui::PushStyleColor(idx, col); } // return true if NOT in disabled mode. -inline bool disabled_modes_check(const std::string& disabled_modes) +inline bool mode_and_tech_check(const std::string& disabled_mode, const std::string& preferred_mode, const std::string& disabled_tech, const std::string& preferred_tech) { - if (disabled_modes.empty()) + if (disabled_mode.empty() && preferred_mode.empty() && disabled_tech.empty() && preferred_tech.empty()) return true; // simple / advanced / expert @@ -43,19 +43,37 @@ inline bool disabled_modes_check(const std::string& disabled_modes) else if (config_mode == ConfigOptionMode::comAdvanced) mode_name = "advanced"; else if (config_mode == ConfigOptionMode::comExpert) mode_name = "expert"; - if (!mode_name.empty() && disabled_modes.find(mode_name) != std::string::npos) + if (!preferred_mode.empty() && !mode_name.empty() && preferred_mode.find(mode_name) == std::string::npos) + return false; + if (!mode_name.empty() && disabled_mode.find(mode_name) != std::string::npos) return false; - // fff / sla + + // tchnology const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); if (tech == ptFFF) { - if (disabled_modes.find("FFF") != std::string::npos) - return false; + // MMU + bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; + if (is_mmu) + { + if (!preferred_tech.empty() && preferred_tech.find("MMU") == std::string::npos) + return false; + if (is_mmu && disabled_tech.find("MMU") != std::string::npos) + return false; + } else { + // only FFF - does not show if MMU preffered + if (!preferred_tech.empty() && preferred_tech.find("FFF") == std::string::npos) + return false; + if (disabled_tech.find("FFF") != std::string::npos) + return false; + } } else { - if (disabled_modes.find("SLA") != std::string::npos) - return false; - } - + // SLA + if (!preferred_tech.empty() && preferred_tech.find("SLA") == std::string::npos) + return false; + if (disabled_tech.find("SLA") != std::string::npos) + return false; + } return true; } } //namespace @@ -96,7 +114,11 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) std::string text1; std::string hypertext_text; std::string follow_text; - std::string disabled_modes; + std::string disabled_mode; + std::string preferred_mode; + std::string disabled_tech; + std::string preferred_tech; + std::string documentation_link; unescape_string_cstyle(_utf8(dict["text"]), fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); @@ -143,8 +165,20 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) text1 = fulltext; } - if (dict.find("disabled_modes") != dict.end()) { - disabled_modes = dict["disabled_modes"]; + if (dict.find("disabled_mode") != dict.end()) { + disabled_mode = dict["disabled_mode"]; + } + if (dict.find("preferred_mode") != dict.end()) { + preferred_mode = dict["preferred_mode"]; + } + if (dict.find("disabled_tech") != dict.end()) { + disabled_tech = dict["disabled_tech"]; + } + if (dict.find("preferred_tech") != dict.end()) { + preferred_tech = dict["preferred_tech"]; + } + if (dict.find("documentation_link") != dict.end()) { + documentation_link = dict["documentation_link"]; } // create HintData @@ -152,37 +186,37 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) //link to internet if(dict["hypertext_type"] == "link") { std::string hypertext_link = dict["hypertext_link"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } }; m_loaded_hints.emplace_back(hint_data); // highlight settings } else if (dict["hypertext_type"] == "settings") { std::string opt = dict["hypertext_settings_opt"]; Preset::Type type = static_cast(std::atoi(dict["hypertext_settings_type"].c_str())); std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; m_loaded_hints.emplace_back(hint_data); // open preferences } else if(dict["hypertext_type"] == "preferences") { int page = static_cast(std::atoi(dict["hypertext_preferences_page"].c_str())); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [page]() { wxGetApp().open_preferences(page); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "plater") { std::string item = dict["hypertext_plater_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gizmo") { std::string item = dict["hypertext_gizmo_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gallery") { - HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; m_loaded_hints.emplace_back(hint_data); } } else { // plain text without hypertext - HintData hint_data{ text1 }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link }; m_loaded_hints.emplace_back(hint_data); } } @@ -194,6 +228,12 @@ HintData* HintDatabase::get_hint(bool up) init(); //return false; } + if (m_loaded_hints.empty()) + { + BOOST_LOG_TRIVIAL(error) << "There were no hints loaded from hints.ini file."; + return nullptr; + } + // shift id m_hint_id = (up ? m_hint_id + 1 : (m_hint_id == 0 ? m_loaded_hints.size() - 1 : m_hint_id - 1)); m_hint_id %= m_loaded_hints.size(); @@ -220,12 +260,16 @@ void NotificationManager::HintNotification::count_spaces() std::string text; text = ImGui::WarningMarker; float picture_width = ImGui::CalcTextSize(text.c_str()).x; - m_left_indentation = picture_width + m_line_height / 2; + m_left_indentation = picture_width * 2 + m_line_height / 2; // no left button picture //m_left_indentation = m_line_height; - m_window_width_offset = m_left_indentation + m_line_height * 3.f;// 5.5f; // no right arrow + if (m_documentation_link.empty()) + m_window_width_offset = m_left_indentation + m_line_height * 3.f; + else + m_window_width_offset = m_left_indentation + m_line_height * 5.5f; + m_window_width = m_line_height * 25; } @@ -387,12 +431,12 @@ void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& i m_window_height += 1 * m_line_height; // top and bottom */ - m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 4.f * m_line_height); + m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 5.f * m_line_height); } bool NotificationManager::HintNotification::on_text_click() { - if (m_hypertext_callback != nullptr && (!m_runtime_disable || disabled_modes_check(m_disabled_modes))) + if (m_hypertext_callback != nullptr && (!m_runtime_disable || mode_and_tech_check(m_disabled_mode, m_preferred_mode, m_disabled_tech, m_preferred_tech))) m_hypertext_callback(); return false; } @@ -405,7 +449,7 @@ void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, con float x_offset = m_left_indentation; int last_end = 0; - float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height :(m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2: m_line_height / 2)); + float starting_y = (m_lines_count < 4 ? m_line_height / 2 * (4 - m_lines_count + 1) : m_line_height / 2); float shift_y = m_line_height; std::string line; @@ -425,8 +469,8 @@ void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, con // regural line line = m_text1.substr(last_end, m_endlines[i] - last_end); } - // first line is headline - if (i == 0) { + // first line is headline (for hint notification it must be divided by \n) + if (m_text1.find('\n') >= m_endlines[i]) { line = ImGui::ColorMarkerStart + line + ImGui::ColorMarkerEnd; } // Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line) @@ -534,6 +578,11 @@ void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& im render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); render_logo(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); render_preferences_button(imgui, win_pos_x, win_pos_y); + if (!m_documentation_link.empty()) + { + render_documentation_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } + } void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) @@ -576,7 +625,6 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp // preferences button is in place of minimize button m_minimize_b_visible = true; } - void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { // Used for debuging @@ -621,12 +669,14 @@ void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, con push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); - std::string button_text; - button_text = ImGui::ErrorMarker;//LeftArrowButton; - - ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); - ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); - ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + std::wstring button_text; + button_text = ImGui::ClippyMarker;//LeftArrowButton; + std::string placeholder_text; + placeholder_text = ImGui::EjectButton; + + ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f * 2.f, button_pic_size.y * 1.25f * 2.f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y * 1.1f); ImGui::SetCursorPosX(0); // shouldnt it render as text? if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) @@ -639,10 +689,81 @@ void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, con ImGui::PopStyleColor(); ImGui::PopStyleColor(); } +void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::wstring button_text; + button_text = ImGui::DocumentationButton; + std::string placeholder_text; + placeholder_text = ImGui::EjectButton; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y), + ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y - 2 * m_line_height), + true)) + { + button_text = ImGui::DocumentationHoverButton; + // tooltip + + long time_now = wxGetLocalTime(); + if (m_hover_time > 0 && m_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Documentation in web browser")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_hover_time == 0) + m_hover_time = time_now; + + } + else + m_hover_time = 0; + + ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + open_documentation(); + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.f, win_size.y - 2 * m_line_height)) + { + open_documentation(); + } + + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); +} + +void NotificationManager::HintNotification::open_documentation() +{ + if (!m_documentation_link.empty()) + { + wxLaunchDefaultBrowser(m_documentation_link); + } +} void NotificationManager::HintNotification::retrieve_data(size_t recursion_counter) { HintData* hint_data = HintDatabase::get_instance().get_hint(true); - if (hint_data != nullptr && !disabled_modes_check(hint_data->disabled_modes)) + if (hint_data == nullptr) + close(); + + if (hint_data != nullptr && !mode_and_tech_check(hint_data->disabled_mode, hint_data->preferred_mode, hint_data->disabled_tech, hint_data->preferred_tech)) { // Content for different user - retrieve another size_t count = HintDatabase::get_instance().get_count(); @@ -661,12 +782,15 @@ void NotificationManager::HintNotification::retrieve_data(size_t recursion_count hint_data->text, hint_data->hypertext, nullptr, hint_data->follow_text }; - update(nd); m_hypertext_callback = hint_data->callback; - m_disabled_modes = hint_data->disabled_modes; + m_disabled_mode = hint_data->disabled_mode; + m_preferred_mode = hint_data->preferred_mode; + m_disabled_tech = hint_data->disabled_tech; + m_preferred_tech = hint_data->preferred_tech; m_runtime_disable = hint_data->runtime_disable; + m_documentation_link = hint_data->documentation_link; m_has_hint_data = true; - + update(nd); } } } //namespace Slic3r diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index 125420fb66..ad0117a820 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -12,9 +12,13 @@ struct HintData std::string text; std::string hypertext; std::string follow_text; - std::string disabled_modes; + std::string disabled_mode; + std::string preferred_mode; + std::string disabled_tech; + std::string preferred_tech; bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode - std::function callback{ nullptr }; + std::string documentation_link; + std::function callback { nullptr }; }; class HintDatabase @@ -77,18 +81,27 @@ protected: void render_right_arrow_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); + void render_documentation_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); void render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); - void retrieve_data(size_t recursion_counter = 0); + void open_documentation(); bool m_has_hint_data { false }; std::function m_hypertext_callback; - std::string m_disabled_modes; + std::string m_disabled_mode; + std::string m_preferred_mode; + std::string m_disabled_tech; + std::string m_preferred_tech; bool m_runtime_disable; + std::string m_documentation_link; float m_close_b_y { 0 }; float m_close_b_w { 0 }; + // hover of buttons + size_t m_hover_time { 0 }; }; } //namespace Slic3r diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 79e34ba859..5b88ad9e7c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -36,7 +36,7 @@ namespace Slic3r { namespace GUI { -static const std::map font_icons = { +static const std::map font_icons = { {ImGui::PrintIconMarker , "cog" }, {ImGui::PrinterIconMarker , "printer" }, {ImGui::PrinterSlaIconMarker , "sla_printer" }, @@ -49,21 +49,27 @@ static const std::map font_icons = { {ImGui::PreferencesButton , "notification_preferences" }, {ImGui::PreferencesHoverButton , "notification_preferences_hover"}, }; -static const std::map font_icons_large = { - {ImGui::CloseNotifButton , "notification_close" }, - {ImGui::CloseNotifHoverButton , "notification_close_hover" }, - {ImGui::EjectButton , "notification_eject_sd" }, - {ImGui::EjectHoverButton , "notification_eject_sd_hover" }, - {ImGui::WarningMarker , "notification_warning" }, - {ImGui::ErrorMarker , "notification_error" }, - {ImGui::CancelButton , "notification_cancel" }, - {ImGui::CancelHoverButton , "notification_cancel_hover" }, - {ImGui::SinkingObjectMarker , "move" }, - {ImGui::CustomSupportsMarker , "fdm_supports" }, - {ImGui::CustomSeamMarker , "seam" }, - {ImGui::MmuSegmentationMarker , "move" }, - {ImGui::VarLayerHeightMarker , "layers" }, - +static const std::map font_icons_large = { + {ImGui::CloseNotifButton , "notification_close" }, + {ImGui::CloseNotifHoverButton , "notification_close_hover" }, + {ImGui::EjectButton , "notification_eject_sd" }, + {ImGui::EjectHoverButton , "notification_eject_sd_hover" }, + {ImGui::WarningMarker , "notification_warning" }, + {ImGui::ErrorMarker , "notification_error" }, + {ImGui::CancelButton , "notification_cancel" }, + {ImGui::CancelHoverButton , "notification_cancel_hover" }, + {ImGui::SinkingObjectMarker , "move" }, + {ImGui::CustomSupportsMarker , "fdm_supports" }, + {ImGui::CustomSeamMarker , "seam" }, + {ImGui::MmuSegmentationMarker , "move" }, + {ImGui::VarLayerHeightMarker , "layers" }, + {ImGui::DocumentationButton , "notification_documentation" }, + {ImGui::DocumentationHoverButton, "notification_documentation_hover"}, +}; + +static const std::map font_icons_extra_large = { + {ImGui::ClippyMarker , "notification_clippy" }, + }; const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; @@ -989,6 +995,8 @@ void ImGuiWrapper::init_font(bool compress) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); for (auto& icon : font_icons_large) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); + for (auto& icon : font_icons_extra_large) + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); // Build texture atlas unsigned char* pixels; @@ -1027,6 +1035,22 @@ void ImGuiWrapper::init_font(bool compress) rect_id++; } + icon_sz *= 2; // default size of extra large icon is 64 px + for (auto icon : font_icons_extra_large) { + if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { + assert(rect->Width == icon_sz); + assert(rect->Height == icon_sz); + std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); + const ImU32* pIn = (ImU32*)raw_data.data(); + for (int y = 0; y < icon_sz; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (int x = 0; x < icon_sz; x++) + *pOut++ = *pIn++; + } + } + rect_id++; + } + // Upload texture to graphics system GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); From d6370a16fafc1efc6b5c19d2341e3ee3697abe68 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 Aug 2021 17:25:20 +0200 Subject: [PATCH 050/240] DoubleSlider: Fix for #6756 - Cannot Color Change (M600) at height when MMU Painting + Fixed update for the ruler when "No sparce layer" option is enabled --- src/slic3r/GUI/DoubleSlider.cpp | 11 +++++++---- src/slic3r/GUI/GUI_Preview.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 9134d6988d..94d6a2c647 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -384,7 +384,7 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one post_ticks_changed_event(); - if (custom_gcode_per_print_z.mode) + if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; Refresh(); @@ -439,7 +439,7 @@ void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, c m_mode = !is_one_extruder_printed_model ? MultiExtruder : only_extruder < 0 ? SingleExtruder : MultiAsSingle; - if (!m_ticks.mode) + if (!m_ticks.mode || (m_ticks.empty() && m_ticks.mode != m_mode)) m_ticks.mode = m_mode; m_only_extruder = only_extruder; @@ -546,7 +546,8 @@ bool Control::is_wipe_tower_layer(int tick) const return false; if (tick == 0 || (tick == (int)m_values.size() - 1 && m_values[tick] > m_values[tick - 1])) return false; - if (m_values[tick - 1] == m_values[tick + 1] && m_values[tick] < m_values[tick + 1]) + if ((m_values[tick - 1] == m_values[tick + 1] && m_values[tick] < m_values[tick + 1]) || + (tick > 0 && m_values[tick] < m_values[tick - 1]) ) // if there is just one wiping on the layer return true; return false; @@ -1078,7 +1079,9 @@ void Control::draw_ruler(wxDC& dc) { if (m_values.empty()) return; - m_ruler.update(this->GetParent(), m_values, get_scroll_step()); + // When "No sparce layer" is enabled, use m_layers_values for ruler update. + // Because of m_values has duplicate values in this case. + m_ruler.update(this->GetParent(), m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); int height, width; get_size(&width, &height); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d17a277d8e..d87c23d81e 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -788,11 +788,11 @@ void Preview::update_layers_slider_mode() object->config.option("extruder")->getInt() != extruder) return false; - if (object->volumes.size() > 1) - for (ModelVolume* volume : object->volumes) - if (volume->config.has("extruder") && - volume->config.option("extruder")->getInt() != extruder) - return false; + for (ModelVolume* volume : object->volumes) + if (volume->config.has("extruder") && + volume->config.option("extruder")->getInt() != extruder || + !volume->mmu_segmentation_facets.empty()) + return false; for (const auto& range : object->layer_config_ranges) if (range.second.has("extruder") && From 9f9cbb46f9415b052db9de0f22ee08ba475c418b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 9 Aug 2021 17:37:25 +0200 Subject: [PATCH 051/240] Suppor of "No extrusion" firmware flavor by not emitting the E axis. Fix of https://github.com/prusa3d/PrusaSlicer/issues/6023 The fix is partial: No extrusions are shown by the final G-code preview. --- src/libslic3r/GCode.cpp | 4 +++- src/libslic3r/GCodeReader.cpp | 21 ++++++++++++++++----- src/libslic3r/GCodeReader.hpp | 3 ++- src/libslic3r/GCodeWriter.cpp | 16 ++++++++++------ src/libslic3r/GCodeWriter.hpp | 1 + 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9e74d217d2..abdbafd31f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2714,7 +2714,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // calculate extrusion length per distance unit double e_per_mm = m_writer.extruder()->e_per_mm3() * path.mm3_per_mm; - if (m_writer.extrusion_axis().empty()) e_per_mm = 0; + if (m_writer.extrusion_axis().empty()) + // gcfNoExtrusion + e_per_mm = 0; // set speed if (speed == -1) { diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 3c76e05c78..7dc3c9a8ac 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -12,16 +12,24 @@ namespace Slic3r { +static inline char get_extrusion_axis_char(const GCodeConfig &config) +{ + std::string axis = get_extrusion_axis(config); + assert(axis.size() <= 1); + // Return 0 for gcfNoExtrusion + return axis.empty() ? 0 : axis[0]; +} + void GCodeReader::apply_config(const GCodeConfig &config) { m_config = config; - m_extrusion_axis = get_extrusion_axis(m_config)[0]; + m_extrusion_axis = get_extrusion_axis_char(m_config); } void GCodeReader::apply_config(const DynamicPrintConfig &config) { m_config.apply(config, true); - m_extrusion_axis = get_extrusion_axis(m_config)[0]; + m_extrusion_axis = get_extrusion_axis_char(m_config); } const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command) @@ -52,9 +60,10 @@ const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, case 'Z': axis = Z; break; case 'F': axis = F; break; default: - if (*c == m_extrusion_axis) - axis = E; - else if (*c >= 'A' && *c <= 'Z') + if (*c == m_extrusion_axis) { + if (m_extrusion_axis != 0) + axis = E; + } else if (*c >= 'A' && *c <= 'Z') // Unknown axis, but we still want to remember that such a axis was seen. axis = UNKNOWN_AXIS; break; @@ -190,6 +199,8 @@ void GCodeReader::GCodeLine::set(const GCodeReader &reader, const Axis axis, con match[1] = 'F'; else { assert(axis == E); + // Extruder axis is set. + assert(reader.extrusion_axis() != 0); match[1] = reader.extrusion_axis(); } diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index ad9b7195a8..6b58608e61 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -122,8 +122,9 @@ public: float& f() { return m_position[F]; } float f() const { return m_position[F]; } + // Returns 0 for gcfNoExtrusion. char extrusion_axis() const { return m_extrusion_axis; } - void set_extrusion_axis(char axis) { m_extrusion_axis = axis; } +// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; } private: const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index bbb45c96d0..24549fd895 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -405,8 +405,10 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: std::ostringstream gcode; gcode << "G1 X" << XYZF_NUM(point(0)) - << " Y" << XYZF_NUM(point(1)) - << " " << m_extrusion_axis << E_NUM(m_extruder->E()); + << " Y" << XYZF_NUM(point(1)); + if (! m_extrusion_axis.empty()) + // not gcfNoExtrusion + gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E()); COMMENT(comment); gcode << "\n"; return gcode.str(); @@ -421,8 +423,10 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std std::ostringstream gcode; gcode << "G1 X" << XYZF_NUM(point(0)) << " Y" << XYZF_NUM(point(1)) - << " Z" << XYZF_NUM(point(2)) - << " " << m_extrusion_axis << E_NUM(m_extruder->E()); + << " Z" << XYZF_NUM(point(2)); + if (! m_extrusion_axis.empty()) + // not gcfNoExtrusion + gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E()); COMMENT(comment); gcode << "\n"; return gcode.str(); @@ -474,7 +478,7 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std gcode << "G22 ; retract\n"; else gcode << "G10 ; retract\n"; - } else { + } else if (! m_extrusion_axis.empty()) { gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) << " F" << XYZF_NUM(m_extruder->retract_speed() * 60.); COMMENT(comment); @@ -503,7 +507,7 @@ std::string GCodeWriter::unretract() else gcode << "G11 ; unretract\n"; gcode << this->reset_e(); - } else { + } else if (! m_extrusion_axis.empty()) { // use G1 instead of G0 because G0 will blend the restart with the previous travel move gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E()) << " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.); diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 3a57c8bd29..2de95ecc5b 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -25,6 +25,7 @@ public: Extruder* extruder() { return m_extruder; } const Extruder* extruder() const { return m_extruder; } + // Returns empty string for gcfNoExtrusion. std::string extrusion_axis() const { return m_extrusion_axis; } void apply_print_config(const PrintConfig &print_config); // Extruders are expected to be sorted in an increasing order. From 04d6b17c572bf732292b84bca875cf2ccb406efb Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 10 Aug 2021 09:41:28 +0200 Subject: [PATCH 052/240] Fix of Some FDM supports fail to generate due to wrongly-translated enforcer polygons. #6739 Thanks @n8bot for finding the bug. Also removed some dead code in MM segmentation. --- src/libslic3r/MultiMaterialSegmentation.cpp | 219 +------------------- src/libslic3r/Print.hpp | 3 + src/libslic3r/PrintObject.cpp | 6 +- src/libslic3r/PrintObjectSlice.cpp | 7 +- 4 files changed, 8 insertions(+), 227 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 95475b0d7c..9456f50775 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1240,221 +1240,6 @@ static inline std::vector> mmu_segmentation_top_and_bott const size_t num_layers = input_expolygons.size(); const ConstLayerPtrsAdaptor layers = print_object.layers(); -#if 0 - auto get_extrusion_width = [&layers = std::as_const(layers)](const size_t layer_idx) -> float { - auto extrusion_width_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region().config().perimeter_extrusion_width < - l2->region().config().perimeter_extrusion_width; - }); - assert(extrusion_width_it != layers[layer_idx]->regions().end()); - return float((*extrusion_width_it)->region().config().perimeter_extrusion_width); - }; - - auto get_top_solid_layers = [&layers = std::as_const(layers)](const size_t layer_idx) -> int { - auto top_solid_layer_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region().config().top_solid_layers < l2->region().config().top_solid_layers; - }); - assert(top_solid_layer_it != layers[layer_idx]->regions().end()); - return (*top_solid_layer_it)->region().config().top_solid_layers; - }; - - auto get_bottom_solid_layers = [&layers = std::as_const(layers)](const size_t layer_idx) -> int { - auto top_bottom_layer_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region().config().bottom_solid_layers < l2->region().config().bottom_solid_layers; - }); - assert(top_bottom_layer_it != layers[layer_idx]->regions().end()); - return (*top_bottom_layer_it)->region().config().bottom_solid_layers; - }; - - std::vector top_layers(num_layers); - top_layers.back() = input_expolygons.back(); - tbb::parallel_for(tbb::blocked_range(1, num_layers), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - throw_on_cancel_callback(); - float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - top_layers[layer_idx - 1] = diff_ex(input_expolygons[layer_idx - 1], offset_ex(input_expolygons[layer_idx], extrusion_width)); - } - }); // end of parallel_for - - std::vector bottom_layers(num_layers); - bottom_layers.front() = input_expolygons.front(); - tbb::parallel_for(tbb::blocked_range(0, num_layers - 1), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - throw_on_cancel_callback(); - float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - bottom_layers[layer_idx + 1] = diff_ex(input_expolygons[layer_idx + 1], offset_ex(input_expolygons[layer_idx], extrusion_width)); - } - }); // end of parallel_for - - std::vector> triangles_by_color_raw(num_extruders, std::vector(layers.size())); - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - begin"; - { - auto delta = float(10 * SCALED_EPSILON); - std::vector deltas { delta, delta, delta }; - Points projected_facet; - for (const ModelVolume *mv : print_object.model_object()->volumes) - if (mv->is_model_part()) { - const Transform3f tr = print_object.trafo().cast() * mv->get_matrix().cast(); - - for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) { - const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); - if (custom_facets.indices.empty()) - continue; - - throw_on_cancel_callback(); - for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { - float min_z = std::numeric_limits::max(); - float max_z = std::numeric_limits::lowest(); - - std::array facet; - for (int p_idx = 0; p_idx < 3; ++p_idx) { - facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; - max_z = std::max(max_z, facet[p_idx].z()); - min_z = std::min(min_z, facet[p_idx].z()); - } - - // Sort the vertices by z-axis for simplification of projected_facet on slices - std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); - projected_facet.clear(); - projected_facet.reserve(3); - for (int p_idx = 0; p_idx < 3; ++p_idx) - projected_facet.emplace_back(Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())) - print_object.center_offset()); - if (cross2((projected_facet[1] - projected_facet[0]).cast(), (projected_facet[2] - projected_facet[1]).cast()) < 0) - // Make CCW. - std::swap(projected_facet[1], projected_facet[2]); - ClipperLib::Path offsetted = mittered_offset_path_scaled(projected_facet, deltas, 3.); - - // Find lowest slice not below the triangle. - auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); - auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); - - if (last_layer == layers.end()) - --last_layer; - - if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON)) - --first_layer; - - for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it) - if (size_t layer_idx = layer_it - layers.begin(); ! top_layers[layer_idx].empty() || ! bottom_layers[layer_idx].empty()) - triangles_by_color_raw[extruder_idx][layer_idx].emplace_back(offsetted); - } - } - } - } - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - end"; - - std::vector> triangles_by_color(num_extruders, std::vector(layers.size())); - tbb::parallel_for(tbb::blocked_range(0, num_layers), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - throw_on_cancel_callback(); - float offset_factor = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) - if (ClipperLib::Paths &src_paths = triangles_by_color_raw[extruder_id][layer_idx]; !src_paths.empty()) - triangles_by_color[extruder_id][layer_idx] = offset_ex(offset_ex(ClipperPaths_to_Slic3rExPolygons(src_paths), -offset_factor), offset_factor); - } - }); // end of parallel_for - triangles_by_color_raw.clear(); - - std::vector> triangles_by_color_bottom(num_extruders); - std::vector> triangles_by_color_top(num_extruders); - triangles_by_color_bottom.assign(num_extruders, std::vector(num_layers)); - triangles_by_color_top.assign(num_extruders, std::vector(num_layers)); - - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - begin"; - for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { - float extrusion_width = scale_(get_extrusion_width(layer_idx)); - int top_solid_layers = get_top_solid_layers(layer_idx); - ExPolygons top_expolygon = top_layers[layer_idx]; - if (top_expolygon.empty()) - continue; - - for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { - throw_on_cancel_callback(); - if (triangles_by_color[color_idx][layer_idx].empty()) - continue; - ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], top_expolygon); - if (!intersection_poly.empty()) { - triangles_by_color_top[color_idx][layer_idx].insert(triangles_by_color_top[color_idx][layer_idx].end(), intersection_poly.begin(), - intersection_poly.end()); - for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) { - float offset_value = float(layer_idx - last_idx) * (-1.0f) * extrusion_width; - if (offset_ex(top_expolygon, offset_value).empty()) - continue; - ExPolygons layer_slices_trimmed = input_expolygons[last_idx]; - - for (int last_idx_1 = last_idx; last_idx_1 < int(layer_idx); ++last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx_1 + 1]); - } - - ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); - ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_top[color_idx][layer_idx], offset_e); - triangles_by_color_top[color_idx][last_idx].insert(triangles_by_color_top[color_idx][last_idx].end(), intersection_poly_2.begin(), - intersection_poly_2.end()); - } - } - } - } - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - end"; - - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - begin"; - for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { - float extrusion_width = scale_(get_extrusion_width(layer_idx)); - int bottom_solid_layers = get_bottom_solid_layers(layer_idx); - const ExPolygons &bottom_expolygon = bottom_layers[layer_idx]; - if (bottom_expolygon.empty()) - continue; - - for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { - throw_on_cancel_callback(); - if (triangles_by_color[color_idx][layer_idx].empty()) - continue; - - ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], bottom_expolygon); - if (!intersection_poly.empty()) { - triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(), - intersection_poly.end()); - for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, num_layers); ++last_idx) { - float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width; - if (offset_ex(bottom_expolygon, offset_value).empty()) - continue; - ExPolygons layer_slices_trimmed = input_expolygons[last_idx]; - for (int last_idx_1 = int(last_idx); last_idx_1 > int(layer_idx); --last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(input_expolygons[last_idx_1 - 1], offset_value)); - } - - ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); - ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_bottom[color_idx][layer_idx], offset_e); - append(triangles_by_color_bottom[color_idx][last_idx], std::move(intersection_poly_2)); - } - } - } - } - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - end"; - - std::vector> triangles_by_color_merged(num_extruders); - triangles_by_color_merged.assign(num_extruders, std::vector(num_layers)); - for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { - throw_on_cancel_callback(); - for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { - auto &self = triangles_by_color_merged[color_idx][layer_idx]; - append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); - append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); - self = union_ex(self); - } - - // Cut all colors for cases when two colors are overlapping - for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { - triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx], - triangles_by_color_merged[color_idx - 1][layer_idx]); - } - } -#else - // Maximum number of top / bottom layers accounts for maximum overlap of one thread group into a neighbor thread group. int max_top_layers = 0; int max_bottom_layers = 0; @@ -1470,8 +1255,7 @@ static inline std::vector> mmu_segmentation_top_and_bott // project downards pointing painted triangles over bottom surfaces. std::vector> top_raw(num_extruders), bottom_raw(num_extruders); std::vector zs = zs_from_layers(print_object.layers()); - Transform3d object_trafo = print_object.trafo(); - object_trafo.pretranslate(Vec3d(- unscale(print_object.center_offset().x()), - unscale(print_object.center_offset().y()), 0)); + Transform3d object_trafo = print_object.trafo_centered(); #ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM static int iRun = 0; @@ -1650,7 +1434,6 @@ static inline std::vector> mmu_segmentation_top_and_bott triangles_by_color_merged[color_idx - 1][layer_idx]); } }); -#endif return triangles_by_color_merged; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1854800cc7..491960705c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -253,6 +253,9 @@ public: ConstLayerPtrsAdaptor layers() const { return ConstLayerPtrsAdaptor(&m_layers); } ConstSupportLayerPtrsAdaptor support_layers() const { return ConstSupportLayerPtrsAdaptor(&m_support_layers); } const Transform3d& trafo() const { return m_trafo; } + // Trafo with the center_offset() applied after the transformation, to center the object in XY before slicing. + Transform3d trafo_centered() const + { Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); return t; } const PrintInstances& instances() const { return m_instances; } // Whoever will get a non-const pointer to PrintObject will be able to modify its layers. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8ebe1eb101..ae98165310 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -428,10 +428,8 @@ std::pair PrintObject::prepare indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set(); // Rotate mesh and build octree on it with axis-aligned (standart base) cubes. - Transform3d m = m_trafo; - m.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); auto to_octree = transform_to_octree().toRotationMatrix(); - its_transform(mesh, to_octree * m, true); + its_transform(mesh, to_octree * this->trafo_centered(), true); // Triangulate internal bridging surfaces. std::vector> overhangs(this->layers().size()); @@ -2298,7 +2296,7 @@ void PrintObject::project_and_append_custom_facets( : mv->supported_facets.get_facets_strict(*mv, type); if (! custom_facets.indices.empty()) project_triangles_to_slabs(this->layers(), custom_facets, - (Eigen::Translation3d(to_3d(unscaled(this->center_offset()), 0.)) * this->trafo() * mv->get_matrix()).cast(), + (this->trafo_centered() * mv->get_matrix()).cast(), seam, out); } } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 818519be49..ad6a23abc1 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -695,11 +695,9 @@ void PrintObject::slice_volumes() } std::vector slice_zs = zs_from_layers(m_layers); - Transform3d trafo = this->trafo(); - trafo.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); std::vector> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs, slice_volumes_inner( - print->config(), this->config(), trafo, + print->config(), this->config(), this->trafo_centered(), this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback), m_config.clip_multipart_objects, throw_on_cancel_callback); @@ -832,8 +830,7 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m const Print *print = this->print(); auto throw_on_cancel_callback = std::function([print](){ print->throw_if_canceled(); }); MeshSlicingParamsEx params; - params.trafo = this->trafo(); - params.trafo.pretranslate(Vec3d(-unscale(m_center_offset.x()), -unscale(m_center_offset.y()), 0)); + params.trafo = this->trafo_centered(); for (; it_volume != it_volume_end; ++ it_volume) if ((*it_volume)->type() == model_volume_type) { std::vector slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback); From 58f83b5e6aa6de23fce28bedd718517a3029f481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 9 Aug 2021 15:39:35 +0200 Subject: [PATCH 053/240] The offset is applied after the elephant food compensation. The value of the brim offset is now also measured from the object after the elephant foot compensation. Thus, the brim offset set to zero means that the brim will touch the object even if elephant foot compensation is enabled. --- src/libslic3r/Brim.cpp | 97 ++++++++++++++++++++++++----------- src/libslic3r/PrintConfig.cpp | 5 +- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 853c5e22e9..e73bed2c9f 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -45,16 +45,38 @@ static float max_brim_width(const ConstPrintObjectPtrsAdaptor &objects) })); } -static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print) +// Returns ExPolygons of the bottom layer of the print object after elephant foot compensation. +static ExPolygons get_print_object_bottom_layer_expolygons(const PrintObject &print_object) { + ExPolygons ex_polygons; + for (LayerRegion *region : print_object.layers().front()->regions()) + Slic3r::append(ex_polygons, offset_ex(offset_ex(region->slices.surfaces, float(SCALED_EPSILON)), -float(SCALED_EPSILON))); + return ex_polygons; +} + +// Returns ExPolygons of bottom layer for every print object in Print after elephant foot compensation. +static std::vector get_print_bottom_layers_expolygons(const Print &print) +{ + std::vector bottom_layers_expolygons; + bottom_layers_expolygons.reserve(print.objects().size()); + for (const PrintObject *object : print.objects()) + bottom_layers_expolygons.emplace_back(get_print_object_bottom_layer_expolygons(*object)); + + return bottom_layers_expolygons; +} + +static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print, const std::vector &bottom_layers_expolygons) +{ + assert(print.objects().size() == bottom_layers_expolygons.size()); Polygons islands; ConstPrintObjectPtrs island_to_object; - for (const PrintObject *object : print.objects()) { + for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) { Polygons islands_object; - islands_object.reserve(object->layers().front()->lslices.size()); - for (const ExPolygon &ex_poly : object->layers().front()->lslices) + islands_object.reserve(bottom_layers_expolygons[print_object_idx].size()); + for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) islands_object.emplace_back(ex_poly.contour); + const PrintObject *object = print.objects()[print_object_idx]; islands.reserve(islands.size() + object->instances().size() * islands_object.size()); for (const PrintInstance &instance : object->instances()) for (Polygon &poly : islands_object) { @@ -110,7 +132,7 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev //FIXME how about the brim type? auto brim_offset = float(scale_(object->config().brim_offset.value)); Polygons islands_object; - for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) { Polygons contour_offset = offset(ex_poly.contour, brim_offset); for (Polygon &poly : contour_offset) poly.douglas_peucker(SCALED_RESOLUTION); @@ -124,8 +146,12 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev return islands; } -static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) +static ExPolygons top_level_outer_brim_area(const Print &print, + const ConstPrintObjectPtrs &top_level_objects_with_brim, + const std::vector &bottom_layers_expolygons, + const float no_brim_offset) { + assert(print.objects().size() == bottom_layers_expolygons.size()); std::unordered_set top_level_objects_idx; top_level_objects_idx.reserve(top_level_objects_with_brim.size()); for (const PrintObject *object : top_level_objects_with_brim) @@ -133,15 +159,16 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint ExPolygons brim_area; ExPolygons no_brim_area; - for (const PrintObject *object : print.objects()) { - const BrimType brim_type = object->config().brim_type.value; - const float brim_offset = scale_(object->config().brim_offset.value); - const float brim_width = scale_(object->config().brim_width.value); - const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); + for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) { + const PrintObject *object = print.objects()[print_object_idx]; + const BrimType brim_type = object->config().brim_type.value; + const float brim_offset = scale_(object->config().brim_offset.value); + const float brim_width = scale_(object->config().brim_width.value); + const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); ExPolygons brim_area_object; ExPolygons no_brim_area_object; - for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) { if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim) append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset))); @@ -166,8 +193,12 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint return diff_ex(brim_area, no_brim_area); } -static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) +static ExPolygons inner_brim_area(const Print &print, + const ConstPrintObjectPtrs &top_level_objects_with_brim, + const std::vector &bottom_layers_expolygons, + const float no_brim_offset) { + assert(print.objects().size() == bottom_layers_expolygons.size()); std::unordered_set top_level_objects_idx; top_level_objects_idx.reserve(top_level_objects_with_brim.size()); for (const PrintObject *object : top_level_objects_with_brim) @@ -176,16 +207,17 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs ExPolygons brim_area; ExPolygons no_brim_area; Polygons holes; - for (const PrintObject *object : print.objects()) { - const BrimType brim_type = object->config().brim_type.value; - const float brim_offset = scale_(object->config().brim_offset.value); - const float brim_width = scale_(object->config().brim_width.value); - const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); + for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) { + const PrintObject *object = print.objects()[print_object_idx]; + const BrimType brim_type = object->config().brim_type.value; + const float brim_offset = scale_(object->config().brim_offset.value); + const float brim_width = scale_(object->config().brim_width.value); + const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); ExPolygons brim_area_object; ExPolygons no_brim_area_object; Polygons holes_object; - for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) { if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) { if (top_outer_brim) no_brim_area_object.emplace_back(ex_poly); @@ -204,7 +236,7 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs append(holes_object, ex_poly.holes); } - append(no_brim_area_object, offset_ex(object->layers().front()->lslices, brim_offset)); + append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_offset)); for (const PrintInstance &instance : object->instances()) { append_and_translate(brim_area, brim_area_object, instance); @@ -236,7 +268,7 @@ static void optimize_polylines_by_reversing(Polylines *polylines) static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_area, float max_connection_length) { if (polylines.empty()) - return Polylines(); + return {}; BoundingBox bbox = get_extents(polylines); bbox.merge(get_extents(brim_area)); @@ -305,16 +337,20 @@ static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_ } } if (end < polylines.size()) - polylines.erase(polylines.begin() + end, polylines.end()); + polylines.erase(polylines.begin() + int(end), polylines.end()); } return std::move(polylines); } -static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, ExtrusionEntityCollection &brim) +static void make_inner_brim(const Print &print, + const ConstPrintObjectPtrs &top_level_objects_with_brim, + const std::vector &bottom_layers_expolygons, + ExtrusionEntityCollection &brim) { + assert(print.objects().size() == bottom_layers_expolygons.size()); Flow flow = print.brim_flow(); - ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, float(flow.scaled_spacing())); + ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing())); Polygons loops; islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare); for (size_t i = 0; !islands_ex.empty(); ++i) { @@ -334,11 +370,12 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_ // Collect islands_area to be merged into the final 1st layer convex hull. ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area) { - Flow flow = print.brim_flow(); - ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print); - Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim); - ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, float(flow.scaled_spacing())); - islands_area = to_polygons(islands_area_ex); + Flow flow = print.brim_flow(); + std::vector bottom_layers_expolygons = get_print_bottom_layers_expolygons(print); + ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons); + Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim); + ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing())); + islands_area = to_polygons(islands_area_ex); Polygons loops; size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing())); @@ -536,7 +573,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); } - make_inner_brim(print, top_level_objects_with_brim, brim); + make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim); return brim; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ed7961ce10..99154f1020 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -491,10 +491,11 @@ void PrintConfigDef::init_fff_params() def = this->add("brim_offset", coFloat); def->label = L("Brim offset"); def->category = L("Skirt and brim"); - def->tooltip = L("The offset of the brim from the printed object."); + def->tooltip = L("The offset of the brim from the printed object. The offset is applied after the elephant foot compensation."); def->sidetext = L("mm"); + def->min = 0; def->mode = comSimple; - def->set_default_value(new ConfigOptionFloat(0)); + def->set_default_value(new ConfigOptionFloat(0.1f)); def = this->add("clip_multipart_objects", coBool); def->label = L("Clip multi-part objects"); From d88ef826cd9be79ef55d735dda5a938a085da3f2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 10 Aug 2021 10:35:44 +0200 Subject: [PATCH 054/240] Fixed Sinking not visible in right panel after project import --- src/slic3r/GUI/Plater.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2abf9a730e..9338f2ded7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2564,6 +2564,10 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode _L("Object too large?")); } + // Now ObjectList uses GLCanvas3D::is_object_sinkin() to show/hide "Sinking" InfoItem, + // so 3D-scene should be updated before object additing to the ObjectList + this->view3D->reload_scene(false, (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH); + for (const size_t idx : obj_idxs) { wxGetApp().obj_list()->add_object_to_list(idx); } From 1eebaa45765db037c82d6a333f94cd1303d72e7c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 10 Aug 2021 11:20:04 +0200 Subject: [PATCH 055/240] Update info items in object list after repair by netfabb, painted data are removed and the info line stays hanging --- src/slic3r/GUI/GUI_ObjectList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 9bbf82039d..2f86f998b7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3953,6 +3953,7 @@ void ObjectList::fix_through_netfabb() wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx); update_item_error_icon(obj_idx, vol_idx); + update_info_items(obj_idx); } void ObjectList::simplify() From a5ebd45d4d463ec2070408a1e79120c3a21b6e6e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 10 Aug 2021 11:21:40 +0200 Subject: [PATCH 056/240] Renamed 'MMU segmentation; to 'Multimaterial painting' --- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 2 +- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index b452b2f4bb..c992813593 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -42,7 +42,7 @@ void GLGizmoMmuSegmentation::on_shutdown() std::string GLGizmoMmuSegmentation::on_get_name() const { // FIXME Lukas H.: Discuss and change shortcut - return (_L("MMU painting") + " [N]").ToUTF8().data(); + return (_L("Multimaterial painting") + " [N]").ToUTF8().data(); } bool GLGizmoMmuSegmentation::on_is_selectable() const diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 61b5d8f432..296c8bfd02 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1363,7 +1363,7 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type) switch (type) { case InfoItemType::CustomSupports: text += _utf8("custom supports."); break; case InfoItemType::CustomSeam: text += _utf8("custom seam."); break; - case InfoItemType::MmuSegmentation: text += _utf8("MMU segmentation."); break; + case InfoItemType::MmuSegmentation: text += _utf8("multimaterial painting."); break; case InfoItemType::VariableLayerHeight: text += _utf8("variable layer height."); break; case InfoItemType::Sinking: text = _utf8("Partially sinking object(s) were loaded."); break; default: text.clear(); break; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index c7208984e5..3a114fd2c7 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -64,7 +64,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent { m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports") : info_type == InfoItemType::CustomSeam ? _L("Paint-on seam") : - info_type == InfoItemType::MmuSegmentation ? _L("Paint-on segmentation") : + info_type == InfoItemType::MmuSegmentation ? _L("Multimaterial painting") : info_type == InfoItemType::Sinking ? _L("Sinking") : _L("Variable layer height"); m_info_item_type = info_type; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9338f2ded7..fc77e9b9d8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6134,7 +6134,8 @@ void Plater::clear_before_change_mesh(int obj_idx) get_notification_manager()->push_notification( NotificationType::CustomSupportsAndSeamRemovedAfterRepair, NotificationManager::NotificationLevel::RegularNotification, - _u8L("Custom supports and seams were removed after repairing the mesh.")); + _u8L("Custom supports, seams and multimaterial painting were " + "removed after repairing the mesh.")); // _u8L("Undo the repair"), // [this, snapshot_time](wxEvtHandler*){ // // Make sure the snapshot is still available and that From c0a00f4e7016685f4afd34aca3605125dff38c66 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 10 Aug 2021 11:42:38 +0200 Subject: [PATCH 057/240] Fix for - Profile save dialog should appear when application is closed and when don't save project is selected Note: show UnsavedChangesDialog even if there is an object in the scene --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 9333efb205..297869ab97 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2059,7 +2059,7 @@ std::vector> GUI_App::get_selected_presets( // to notify the user whether he is aware that some preset changes will be lost. bool GUI_App::check_and_save_current_preset_changes(const wxString& header) { - if (this->plater()->model().objects.empty() && has_current_preset_changes()) { + if (/*this->plater()->model().objects.empty() && */has_current_preset_changes()) { UnsavedChangesDialog dlg(header); if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL) return false; From ed25d5c53de20a86f9c719dabf709e36c71a53f9 Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko Date: Tue, 10 Aug 2021 12:32:00 +0200 Subject: [PATCH 058/240] Fix for #6692 - Slice button not appearing after infill change --- src/slic3r/GUI/Field.cpp | 88 +++++++++++++++++++++++++--------------- src/slic3r/GUI/Field.hpp | 4 ++ 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 49771081d2..76fd857fb0 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -935,7 +935,7 @@ void Choice::BUILD() { choice_ctrl* temp; if (m_opt.gui_type != ConfigOptionDef::GUIType::undefined && m_opt.gui_type != ConfigOptionDef::GUIType::select_open) { m_is_editable = true; - temp = new choice_ctrl(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); + temp = new choice_ctrl(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, nullptr, wxTE_PROCESS_ENTER); } else { #ifdef __WXOSX__ @@ -988,46 +988,70 @@ void Choice::BUILD() { temp->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { on_change_field(); }, temp->GetId()); if (m_is_editable) { - temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { + temp->Bind(wxEVT_KILL_FOCUS, [this](wxEvent& e) { e.Skip(); - if (m_opt.type == coStrings) { - on_change_field(); + if (bKilledFocus) return; - } - if (is_defined_input_value(window, m_opt.type)) { - switch (m_opt.type) { - case coFloatOrPercent: - { - std::string old_val = !m_value.empty() ? boost::any_cast(m_value) : ""; - if (old_val == boost::any_cast(get_value())) - return; - break; - } - case coInt: - { - int old_val = !m_value.empty() ? boost::any_cast(m_value) : 0; - if (old_val == boost::any_cast(get_value())) - return; - break; - } - default: - { - double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; - if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) - return; - } - } - on_change_field(); - } + bKilledFocus = true; + + if (bEnterPressed) + bEnterPressed = false; else - on_kill_focus(); - }), temp->GetId()); + propagate_value(); + // After processing of KILL_FOCUS event we should to invalidate a bKilledFocus flag + bKilledFocus = false; + } ); + + temp->Bind(wxEVT_TEXT_ENTER, [this, temp](wxEvent& e) { +#ifdef _WIN32 + temp->SetFocus(); +#else + bEnterPressed = true; + propagate_value(); +#endif //_WIN32 + } ); } temp->SetToolTip(get_tooltip_text(temp->GetValue())); } +void Choice::propagate_value() +{ + if (m_opt.type == coStrings) { + on_change_field(); + return; + } + + if (is_defined_input_value(window, m_opt.type)) { + switch (m_opt.type) { + case coFloatOrPercent: + { + std::string old_val = !m_value.empty() ? boost::any_cast(m_value) : ""; + if (old_val == boost::any_cast(get_value())) + return; + break; + } + case coInt: + { + int old_val = !m_value.empty() ? boost::any_cast(m_value) : 0; + if (old_val == boost::any_cast(get_value())) + return; + break; + } + default: + { + double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; + if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + return; + } + } + on_change_field(); + } + else + on_kill_focus(); +} + void Choice::suppress_scroll() { m_suppress_scroll = true; diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 0693091ade..34a2481b52 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -342,6 +342,8 @@ public: class Choice : public Field { using Field::Field; + bool bKilledFocus = false; + public: Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} @@ -349,6 +351,8 @@ public: wxWindow* window{ nullptr }; void BUILD() override; + // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER + void propagate_value(); /* Under OSX: wxBitmapComboBox->GetWindowStyle() returns some weard value, * so let use a flag, which has TRUE value for a control without wxCB_READONLY style From 06d27bcb3c333b0e4a20f2f126bc972eedb92607 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 10 Aug 2021 14:48:01 +0200 Subject: [PATCH 059/240] Hints notification Hints notification: enabled / disabled tags Hints notification: suppress opening url due to preferences. Hint notification: close after 5 minutes and button in Help menu --- resources/data/hints.ini | 50 +++--- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/HintNotification.cpp | 238 ++++++++++++++++--------- src/slic3r/GUI/HintNotification.hpp | 23 ++- src/slic3r/GUI/MainFrame.cpp | 5 + src/slic3r/GUI/NotificationManager.cpp | 24 ++- src/slic3r/GUI/NotificationManager.hpp | 3 +- 7 files changed, 215 insertions(+), 130 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 81817f3ca4..0de06f04a7 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -40,13 +40,13 @@ # hypertext_type = gallery # # -# Each notification can have disabled and preferred modes and techs - divided by ; -# preferred_mode = simple -# disabled_mode = advanced; expert -# preferred_tech = SLA -# disabled_tech = FFF; MMU -# Algorithm shows hint only if in preffered mode / tech. -# Algorithm shows hint only if not in disabled mode / tech. +# Each notification can have disabled and enabled modes and techs - divided by ; and space +# enabled_tags = ... +# disabled_tags = ... +# supported tags are: simple; advanced; expert; FFF; MMU; SLA +# FFF is affirmative for both one or more extruder printers. +# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) +# Algorithm shows hint only if not in all disabled tags. # if there are both disabled and preferred, only preferred that are not in disabled are valid. @@ -70,7 +70,7 @@ hypertext_type = settings hypertext_settings_opt = infill_every_layers hypertext_settings_type = 1 hypertext_settings_category = Infill -disabled_modes = SLA; simple +disabled_tags = SLA; simple [hint:Hiding sidebar] text = Hiding sidebar\nDid you know that you can hide the right sidebar using the shortcut Shift+Tab? You can also enable the icon for this from thePreferences. @@ -81,7 +81,7 @@ hypertext_preferences_page = 2 text = Variable layer height\nDid you know that you can print different regions of your model with a different layer height and smooth the transitions between them? Try theVariable layer height tool.(Not available for SLA printers.) hypertext_type = plater hypertext_plater_item = layersediting -disabled_modes = SLA +disabled_tags = SLA [hint:Undo/redo history] text = Undo/redo history\nDid you know that you can right-click theundo/redo arrowsto see the history of changes and to undo or redo several actions at once? @@ -94,12 +94,12 @@ hypertext_type = plater hypertext_plater_item = arrange [hint:Reload from disk] -text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in thedocumentation. +text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 [hint:Different layer height for each model] -text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in thedocumentation. +text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 @@ -109,7 +109,7 @@ hypertext_type = settings hypertext_settings_opt = solid_infill_below_area hypertext_settings_type = 1 hypertext_settings_category = Infill -disabled_modes = SLA; simple; advanced +enabled_tags = FFF; expert [hint:Search functionality] text = Search functionality\n Did you know that you use theSearchtool to quickly find a specific PrusaSlicer setting? Or use the familiar shortcut Ctrl+F. @@ -140,10 +140,10 @@ text = PageUp / PageDown quick rotation by 45 degrees\nDid you know that you can text = Load config from G-code\nDid you know that you can use File-Import Config to load print, filament and printer profiles from an existing G-code file? Similarly, you can use File-Import SL1 archive, which also lets you reconstruct 3D models from the voxel data. [hint:Ironing] -text = Ironing\nDid you know that you can smooth top surfaces of prints using Ironing? The nozzle will run a special second infill phase at the same layer to fill in holes and flatten any lifted plastic. Read more in thedocumentation. (Requires Advanced or Expert mode.) +text = Ironing\nDid you know that you can smooth top surfaces of prints using Ironing? The nozzle will run a special second infill phase at the same layer to fill in holes and flatten any lifted plastic. Read more in the documentation. (Requires Advanced or Expert mode.) hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/ironing_177488 -disabled_modes = SLA; simple +disabled_tags = SLA; simple [hint:Fuzzy skin] text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model. @@ -151,38 +151,38 @@ hypertext_type = settings hypertext_settings_opt = fuzzy_skin hypertext_settings_type = 1 hypertext_settings_category = Layers and perimeters -disabled_modes = SLA +disabled_tags = SLA [hint:Negative volume] -text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in thedocumentation.(Requires Advanced or Expert mode.) +text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in the documentation. (Requires Advanced or Expert mode.) hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 -disabled_modes = SLA; simple +disabled_tags = SLA; simple [hint:Paint-on supports] text = Paint-on supports\nDid you know that you can paint directly on the object and select areas, where supports should be enforced or blocked? Try thePaint-on supportsfeature. (Requires Advanced or Expert mode.) hypertext_type = gizmo hypertext_gizmo_item = fdm_supports -disabled_modes = SLA; simple +disabled_tags = SLA; simple [hint:Paint-on seam] text = Paint-on seam\nDid you know that you can paint directly on the object and select where to place the start/endpoint of each perimeter loop? Try theSeam paintingfeature. (Requires Advanced or Expert mode.) hypertext_type = gizmo hypertext_gizmo_item = seam -disabled_modes = SLA; simple +disabled_tags = SLA; simple [hint:Insert Pause] -text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in thedocumentation. +text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer [hint:Insert Custom G-code] -text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in thedocumentation. +text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer [hint:Configuration snapshots] -text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - Configuration snapshots menu. Read more in thedocumentation. +text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - Configuration snapshots menu. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 @@ -192,7 +192,7 @@ hypertext_type = settings hypertext_settings_opt = top_solid_min_thickness hypertext_settings_type = 1 hypertext_settings_category = Layers and perimeters -disabled_modes = SLA +disabled_tags = SLA [hint:Settings in non-modal window] text = Settings in non-modal window\nDid you know that you can open the Settings in a new non-modal window? This means you can have settings open on one screen and the G-code Preview on the other. Go to thePreferencesand select Settings in non-modal window. @@ -200,7 +200,7 @@ hypertext_type = preferences hypertext_preferences_page = 2 [hint:Adaptive infills] -text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in thedocumentation. +text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 @@ -208,7 +208,7 @@ documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. [hint:Simplify mesh] -text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in thedocumentation. +text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 297869ab97..4fa80bf58a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -664,7 +664,7 @@ void GUI_App::post_init() // show "Did you know" notification if (app_config->get("show_hints") == "1" && ! is_gcode_viewer()) - plater_->get_notification_manager()->push_hint_notification(); + plater_->get_notification_manager()->push_hint_notification(true); // The extra CallAfter() is needed because of Mac, where this is the only way // to popup a modal dialog on start without screwing combo boxes. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index a4ef5b963a..973359bcbc 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -30,52 +30,116 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f else ImGui::PushStyleColor(idx, col); } -// return true if NOT in disabled mode. -inline bool mode_and_tech_check(const std::string& disabled_mode, const std::string& preferred_mode, const std::string& disabled_tech, const std::string& preferred_tech) +enum TagCheckResult { - if (disabled_mode.empty() && preferred_mode.empty() && disabled_tech.empty() && preferred_tech.empty()) - return true; + TagCheckAffirmative, + TagCheckNegative, + TagCheckNotCompatible +}; +// returns if in mode defined by tag +inline TagCheckResult tag_check_mode(const std::string& tag) +{ + std::vector allowed_tags = {"simple", "advanced", "expert"}; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) + { + ConfigOptionMode config_mode = wxGetApp().get_mode(); + if (config_mode == ConfigOptionMode::comSimple) return (tag == "simple" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comAdvanced) return (tag == "advanced" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comExpert) return (tag == "expert" ? TagCheckAffirmative : TagCheckNegative); + } + return TagCheckNotCompatible; +} - // simple / advanced / expert - ConfigOptionMode config_mode = wxGetApp().get_mode(); - std::string mode_name; - if (config_mode == ConfigOptionMode::comSimple) mode_name = "simple"; - else if (config_mode == ConfigOptionMode::comAdvanced) mode_name = "advanced"; - else if (config_mode == ConfigOptionMode::comExpert) mode_name = "expert"; - - if (!preferred_mode.empty() && !mode_name.empty() && preferred_mode.find(mode_name) == std::string::npos) - return false; - if (!mode_name.empty() && disabled_mode.find(mode_name) != std::string::npos) - return false; - - - // tchnology - const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); - if (tech == ptFFF) { - // MMU - bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; - if (is_mmu) - { - if (!preferred_tech.empty() && preferred_tech.find("MMU") == std::string::npos) - return false; - if (is_mmu && disabled_tech.find("MMU") != std::string::npos) - return false; +inline TagCheckResult tag_check_tech(const std::string& tag) +{ + std::vector allowed_tags = { "FFF", "MMU", "SLA" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { + const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (tech == ptFFF) { + // MMU / FFF + bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; + if (tag == "MMU") return (is_mmu ? TagCheckAffirmative : TagCheckNegative); + return (tag == "FFF" ? TagCheckAffirmative : TagCheckNegative); } else { - // only FFF - does not show if MMU preffered - if (!preferred_tech.empty() && preferred_tech.find("FFF") == std::string::npos) - return false; - if (disabled_tech.find("FFF") != std::string::npos) - return false; + // SLA + return (tag == "SLA" ? TagCheckAffirmative : TagCheckNegative); + } + } + return TagCheckNotCompatible; +} + +// return true if NOT in disabled mode. +inline bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) +{ + if (disabled_tags.empty() && enabled_tags.empty()) + return true; + // enabled tags must ALL return affirmative or check fails + if (!enabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < enabled_tags.size(); i++) { + if (enabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (enabled_tags[i] != ';') { + tag += enabled_tags[i]; + } + if (enabled_tags[i] == ';' || i == enabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in enabled_tags not compatible."; + // non compatible in enabled means return false since all enabled must be affirmative. + return false; + } + } + } + } + // disabled tags must all NOT return affirmative or check fails + if (!disabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < disabled_tags.size(); i++) { + if (disabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (disabled_tags[i] != ';') { + tag += disabled_tags[i]; + } + if (disabled_tags[i] == ';' || i == disabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in disabled_tags not compatible."; + } + } } - } else { - // SLA - if (!preferred_tech.empty() && preferred_tech.find("SLA") == std::string::npos) - return false; - if (disabled_tech.find("SLA") != std::string::npos) - return false; } return true; } +inline void launch_browser_if_allowed(const std::string& url) +{ + if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") + wxLaunchDefaultBrowser(url); +} } //namespace void HintDatabase::init() @@ -108,17 +172,15 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) dict.emplace(data.first, data.second.data()); } - //unescaping a translating all texts - //unescape text1 + //unescaping and translating all texts and saving all data common for all hint types std::string fulltext; std::string text1; std::string hypertext_text; std::string follow_text; - std::string disabled_mode; - std::string preferred_mode; - std::string disabled_tech; - std::string preferred_tech; + std::string disabled_tags; + std::string enabled_tags; std::string documentation_link; + //unescape text1 unescape_string_cstyle(_utf8(dict["text"]), fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); @@ -165,17 +227,11 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) text1 = fulltext; } - if (dict.find("disabled_mode") != dict.end()) { - disabled_mode = dict["disabled_mode"]; + if (dict.find("disabled_tags") != dict.end()) { + disabled_tags = dict["disabled_tags"]; } - if (dict.find("preferred_mode") != dict.end()) { - preferred_mode = dict["preferred_mode"]; - } - if (dict.find("disabled_tech") != dict.end()) { - disabled_tech = dict["disabled_tech"]; - } - if (dict.find("preferred_tech") != dict.end()) { - preferred_tech = dict["preferred_tech"]; + if (dict.find("enabled_tags") != dict.end()) { + enabled_tags = dict["enabled_tags"]; } if (dict.find("documentation_link") != dict.end()) { documentation_link = dict["documentation_link"]; @@ -186,37 +242,37 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) //link to internet if(dict["hypertext_type"] == "link") { std::string hypertext_link = dict["hypertext_link"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } }; m_loaded_hints.emplace_back(hint_data); // highlight settings } else if (dict["hypertext_type"] == "settings") { std::string opt = dict["hypertext_settings_opt"]; Preset::Type type = static_cast(std::atoi(dict["hypertext_settings_type"].c_str())); std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; m_loaded_hints.emplace_back(hint_data); // open preferences } else if(dict["hypertext_type"] == "preferences") { int page = static_cast(std::atoi(dict["hypertext_preferences_page"].c_str())); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "plater") { std::string item = dict["hypertext_plater_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gizmo") { std::string item = dict["hypertext_gizmo_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gallery") { - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; m_loaded_hints.emplace_back(hint_data); } } else { // plain text without hypertext - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link }; m_loaded_hints.emplace_back(hint_data); } } @@ -235,7 +291,7 @@ HintData* HintDatabase::get_hint(bool up) } // shift id - m_hint_id = (up ? m_hint_id + 1 : (m_hint_id == 0 ? m_loaded_hints.size() - 1 : m_hint_id - 1)); + m_hint_id = (up ? m_hint_id + 1 : m_hint_id ); m_hint_id %= m_loaded_hints.size(); AppConfig* app_config = wxGetApp().app_config; @@ -307,7 +363,7 @@ void NotificationManager::HintNotification::count_lines() } // when one word longer than line. if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset || - ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3 + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 5 * 3 ) { float width_of_a = ImGui::CalcTextSize("a").x; int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a); @@ -377,7 +433,7 @@ void NotificationManager::HintNotification::count_lines() } // when one word longer than line. if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset - size_of_last_line || - ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 4 * 3 + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 5 * 3 ) { float width_of_a = ImGui::CalcTextSize("a").x; int letter_count = (int)((m_window_width - m_window_width_offset - size_of_last_line) / width_of_a); @@ -436,7 +492,7 @@ void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& i bool NotificationManager::HintNotification::on_text_click() { - if (m_hypertext_callback != nullptr && (!m_runtime_disable || mode_and_tech_check(m_disabled_mode, m_preferred_mode, m_disabled_tech, m_preferred_tech))) + if (m_hypertext_callback != nullptr && (!m_runtime_disable || tags_check(m_disabled_tags, m_enabled_tags))) m_hypertext_callback(); return false; } @@ -575,10 +631,10 @@ void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& im ImGui::PopStyleColor(); - render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + //render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); render_logo(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); render_preferences_button(imgui, win_pos_x, win_pos_y); - if (!m_documentation_link.empty()) + if (!m_documentation_link.empty() && wxGetApp().app_config->get("suppress_hyperlinks") != "1") { render_documentation_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); } @@ -597,12 +653,24 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp std::string button_text; button_text = ImGui::PreferencesButton; //hover - if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1), + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 15.f, win_pos_y + m_window_height - 1.75f * m_line_height), ImVec2(win_pos_x, win_pos_y + m_window_height), true)) { button_text = ImGui::PreferencesHoverButton; - } + // tooltip + long time_now = wxGetLocalTime(); + if (m_prefe_hover_time > 0 && m_prefe_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Preferences.")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_prefe_hover_time == 0) + m_prefe_hover_time = time_now; + } else + m_prefe_hover_time = 0; ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); @@ -710,21 +778,19 @@ void NotificationManager::HintNotification::render_documentation_button(ImGuiWra { button_text = ImGui::DocumentationHoverButton; // tooltip - long time_now = wxGetLocalTime(); - if (m_hover_time > 0 && m_hover_time < time_now) { + if (m_docu_hover_time > 0 && m_docu_hover_time < time_now) { ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); ImGui::BeginTooltip(); - imgui.text(_u8L("Open Documentation in web browser")); + imgui.text(_u8L("Open Documentation in web browser.")); ImGui::EndTooltip(); ImGui::PopStyleColor(); } - if (m_hover_time == 0) - m_hover_time = time_now; - + if (m_docu_hover_time == 0) + m_docu_hover_time = time_now; } else - m_hover_time = 0; + m_docu_hover_time = 0; ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); @@ -754,16 +820,16 @@ void NotificationManager::HintNotification::open_documentation() { if (!m_documentation_link.empty()) { - wxLaunchDefaultBrowser(m_documentation_link); + launch_browser_if_allowed(m_documentation_link); } } -void NotificationManager::HintNotification::retrieve_data(size_t recursion_counter) +void NotificationManager::HintNotification::retrieve_data(int recursion_counter) { - HintData* hint_data = HintDatabase::get_instance().get_hint(true); + HintData* hint_data = HintDatabase::get_instance().get_hint(recursion_counter >= 0 ? true : false); if (hint_data == nullptr) close(); - if (hint_data != nullptr && !mode_and_tech_check(hint_data->disabled_mode, hint_data->preferred_mode, hint_data->disabled_tech, hint_data->preferred_tech)) + if (hint_data != nullptr && !tags_check(hint_data->disabled_tags, hint_data->enabled_tags)) { // Content for different user - retrieve another size_t count = HintDatabase::get_instance().get_count(); @@ -783,13 +849,11 @@ void NotificationManager::HintNotification::retrieve_data(size_t recursion_count hint_data->hypertext, nullptr, hint_data->follow_text }; m_hypertext_callback = hint_data->callback; - m_disabled_mode = hint_data->disabled_mode; - m_preferred_mode = hint_data->preferred_mode; - m_disabled_tech = hint_data->disabled_tech; - m_preferred_tech = hint_data->preferred_tech; - m_runtime_disable = hint_data->runtime_disable; + m_disabled_tags = hint_data->disabled_tags; + m_enabled_tags = hint_data->enabled_tags; + m_runtime_disable = hint_data->runtime_disable; m_documentation_link = hint_data->documentation_link; - m_has_hint_data = true; + m_has_hint_data = true; update(nd); } } diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index ad0117a820..ff81c66a2a 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -12,10 +12,8 @@ struct HintData std::string text; std::string hypertext; std::string follow_text; - std::string disabled_mode; - std::string preferred_mode; - std::string disabled_tech; - std::string preferred_tech; + std::string disabled_tags; + std::string enabled_tags; bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode std::string documentation_link; std::function callback { nullptr }; @@ -57,12 +55,13 @@ private: class NotificationManager::HintNotification : public NotificationManager::PopNotification { public: - HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) + HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool new_hint) : PopNotification(n, id_provider, evt_handler) { - retrieve_data(); + retrieve_data(new_hint ? 0 : -1); } virtual void init() override; + void open_next() { retrieve_data(0); } protected: virtual void set_next_window_size(ImGuiWrapper& imgui) override; virtual void count_spaces() override; @@ -87,21 +86,21 @@ protected: void render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); - void retrieve_data(size_t recursion_counter = 0); + // recursion counter -1 tells to retrieve same hint as last time + void retrieve_data(int recursion_counter = 0); void open_documentation(); bool m_has_hint_data { false }; std::function m_hypertext_callback; - std::string m_disabled_mode; - std::string m_preferred_mode; - std::string m_disabled_tech; - std::string m_preferred_tech; + std::string m_disabled_tags; + std::string m_enabled_tags; bool m_runtime_disable; std::string m_documentation_link; float m_close_b_y { 0 }; float m_close_b_w { 0 }; // hover of buttons - size_t m_hover_time { 0 }; + size_t m_docu_hover_time { 0 }; + size_t m_prefe_hover_time{ 0 }; }; } //namespace Slic3r diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5cf0d8573f..6a900714ea 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -45,6 +45,7 @@ #include "GUI_Factories.hpp" #include "GUI_ObjectList.hpp" #include "GalleryDialog.hpp" +#include "NotificationManager.hpp" #ifdef _WIN32 #include @@ -1073,6 +1074,10 @@ static wxMenu* generate_help_menu() else append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), GCODEVIEWER_APP_NAME), _L("Show about dialog"), [](wxCommandEvent&) { Slic3r::GUI::about(); }); + append_menu_item(helpMenu, wxID_ANY, _L("Next Hint notification"), _L("Opens another Hint notification."), + [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(true); }); + append_menu_item(helpMenu, wxID_ANY, _L("Reopen Hint notification"), _L("Opens Hint notification in bottom right corner."), + [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(false); }); helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"), [](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 296c8bfd02..9620f803e5 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1347,14 +1347,30 @@ void NotificationManager::upload_job_notification_show_error(int id, const std:: } } } -void NotificationManager::push_hint_notification() +void NotificationManager::push_hint_notification(bool open_next) +{ + + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::DidYouKnowHint) { + if (open_next) + (dynamic_cast(notification.get()))->open_next(); + else + notification->set_hovered(); + return; + } + } + + NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 300, "" }; + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, open_next), 0); +} + +bool NotificationManager::is_hint_notification_open() { for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::DidYouKnowHint) - return; + return true; } - NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 0, "" }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler), 0); + return false; } void NotificationManager::push_updated_item_info_notification(InfoItemType type) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2c5c85b193..72e8677048 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -169,7 +169,8 @@ public: void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); // Hint (did you know) notification - void push_hint_notification(); + void push_hint_notification(bool open_next); + bool is_hint_notification_open(); void push_updated_item_info_notification(InfoItemType type); // Close old notification ExportFinished. void new_export_began(bool on_removable); From ad602274499ca454c3c4d27abfa8b14087e04c44 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 10 Aug 2021 15:08:04 +0200 Subject: [PATCH 060/240] MSW specific: Fixed a typo caused ignoring ENTER for TextControls in Settings Tabs + Added Info icon for MessageDialog --- resources/icons/info.svg | 71 ++++++++++++++++++++++++++++++++++++ src/slic3r/GUI/Field.cpp | 2 +- src/slic3r/GUI/MsgDialog.cpp | 2 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 resources/icons/info.svg diff --git a/resources/icons/info.svg b/resources/icons/info.svg new file mode 100644 index 0000000000..276b260610 --- /dev/null +++ b/resources/icons/info.svg @@ -0,0 +1,71 @@ + +image/svg+xml + + + + + + + + + + diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 76fd857fb0..d7d4745319 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -500,7 +500,7 @@ void TextCtrl::BUILD() { temp->SetToolTip(get_tooltip_text(text_value)); - if (style == wxTE_PROCESS_ENTER) { + if (style & wxTE_PROCESS_ENTER) { temp->Bind(wxEVT_TEXT_ENTER, ([this, temp](wxEvent& e) { #if !defined(__WXGTK__) diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 4e80052426..92b31cc520 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -127,7 +127,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin else { wxClientDC dc(parent); wxSize msg_sz = dc.GetMultiLineTextExtent(msg); - page_size = wxSize(std::min(msg_sz.GetX() + em, 68 * em), + page_size = wxSize(std::min(msg_sz.GetX() + 2 * em, 68 * em), std::min(msg_sz.GetY() + 2 * em, 68 * em)); } html->SetMinSize(page_size); From 9e32a00efc630df2c08a6a0aba7dc7513a22e099 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 10 Aug 2021 15:17:35 +0200 Subject: [PATCH 061/240] Fix of incorrect volume scaling after fix by Netfabb, see the comment in the code as to why it happened --- src/slic3r/Utils/FixModelByWin10.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index cfe8b68b37..a3689d9276 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -37,7 +37,7 @@ #include "../GUI/I18N.hpp" #include "../GUI/MsgDialog.hpp" #include "../GUI/GUI_App.hpp" -#include "../GUI/Mainframe.hpp" +#include "../GUI/MainFrame.hpp" #include #include @@ -364,9 +364,17 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); path_src += ".3mf"; Model model; - ModelObject *model_object = model.add_object(); - model_object->add_volume(*volumes[ivolume]); - model_object->add_instance(); + ModelObject *mo = model.add_object(); + mo->add_volume(*volumes[ivolume]); + + // We are about to save a 3mf, fix it by netfabb and load the fixed 3mf back. + // store_3mf currently bakes the volume transformation into the mesh itself. + // If we then loaded the repaired 3mf and pushed the mesh into the original ModelVolume + // (which remembers the matrix the whole time), the transformation would be used twice. + // We will therefore set the volume transform on the dummy ModelVolume to identity. + mo->volumes.back()->set_transformation(Geometry::Transformation()); + + mo->add_instance(); if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false, nullptr, false)) { boost::filesystem::remove(path_src); throw Slic3r::RuntimeError(L("Export of a temporary 3mf file failed")); From cf32b56454b13faa72bb75363ab63e1d66ba7d9f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 10 Aug 2021 15:21:53 +0200 Subject: [PATCH 062/240] Implemented extension of the G-code post-processor framework: 1) New environment variable SLIC3R_PP_HOST contains one of "File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ... 2) New environment variable SLIC3R_PP_OUTPUT_NAME contains the name of the G-code file including path (for SLIC3R_PP_HOST == "File") or a name of the file after upload to the host (PrusaLink, Octoprint ...) 3) The post-processing script may suggest a new output file name (likely based on SLIC3R_PP_OUTPUT_NAME) by saving it as a single line into a new "output name" temp file. The "output name" file name is created by suffixing the input G-code file name with ".output_name". Please note that the G-code viewer visualizes G-code before post-processing. Fixes Broken PostProcessing when script changes out-filename #6042 --- src/libslic3r/GCode/GCodeProcessor.hpp | 6 +- src/libslic3r/GCode/PostProcessor.cpp | 128 +++++++++++++++---- src/libslic3r/GCode/PostProcessor.hpp | 18 ++- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 133 +++++++++++--------- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 5 +- 5 files changed, 207 insertions(+), 83 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index d83c39d09b..999af481de 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -337,9 +337,9 @@ namespace Slic3r { std::string printer; void reset() { - print = ""; - filament = std::vector(); - printer = ""; + print.clear(); + filament.clear(); + printer.clear(); } }; std::string filename; diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 9a66e743bc..2ffffc7d6d 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -1,5 +1,7 @@ #include "PostProcessor.hpp" +#include "libslic3r/Utils.hpp" + #include #include #include @@ -179,41 +181,125 @@ static int run_script(const std::string &script, const std::string &gcode, std:: namespace Slic3r { -void run_post_process_scripts(const std::string &path, const DynamicPrintConfig &config) +// Run post processing script / scripts if defined. +// Returns true if a post-processing script was executed. +// Returns false if no post-processing script was defined. +// Throws an exception on error. +// host is one of "File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ... +// For a "File" target, a temp file will be created for src_path by adding a ".pp" suffix and src_path will be updated. +// In that case the caller is responsible to delete the temp file created. +// output_name is the final name of the G-code on SD card or when uploaded to PrusaLink or OctoPrint. +// If uploading to PrusaLink or OctoPrint, then the file will be renamed to output_name first on the target host. +// The post-processing script may change the output_name. +bool run_post_process_scripts(std::string &src_path, bool make_copy, const std::string &host, std::string &output_name, const DynamicPrintConfig &config) { - const auto* post_process = config.opt("post_process"); + const auto *post_process = config.opt("post_process"); if (// likely running in SLA mode post_process == nullptr || // no post-processing script post_process->values.empty()) - return; + return false; + + std::string path; + if (make_copy) { + // Don't run the post-processing script on the input file, it will be memory mapped by the G-code viewer. + // Make a copy. + path = src_path + ".pp"; + // First delete an old file if it exists. + try { + if (boost::filesystem::exists(path)) + boost::filesystem::remove(path); + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting an old temporary file %1% before running a post-processing script: %2%", path, err.what()); + } + // Second make a copy. + std::string error_message; + if (copy_file(src_path, path, error_message, false) != SUCCESS) + throw Slic3r::RuntimeError(Slic3r::format("Failed making a temporary copy of G-code file %1% before running a post-processing script: %2%", src_path, error_message)); + } else { + // Don't make a copy of the G-code before running the post-processing script. + path = src_path; + } + + auto delete_copy = [&path, &src_path, make_copy]() { + if (make_copy) + try { + if (boost::filesystem::exists(path)) + boost::filesystem::remove(path); + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting a temporary copy %1% of a G-code file %2% : %3%", path, src_path, err.what()); + } + }; - // Store print configuration into environment variables. - config.setenv_(); auto gcode_file = boost::filesystem::path(path); if (! boost::filesystem::exists(gcode_file)) throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file")); - for (const std::string &scripts : post_process->values) { - std::vector lines; - boost::split(lines, scripts, boost::is_any_of("\r\n")); - for (std::string script : lines) { - // Ignore empty post processing script lines. - boost::trim(script); - if (script.empty()) - continue; - BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path; + // Store print configuration into environment variables. + config.setenv_(); + // Let the post-processing script know the target host ("File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ...) + boost::nowide::setenv("SLIC3R_PP_HOST", host.c_str(), 1); + // Let the post-processing script know the final file name. For "File" host, it is a full path of the target file name and its location, for example pointing to an SD card. + // For "PrusaLink" or "OctoPrint", it is a file name optionally with a directory on the target host. + boost::nowide::setenv("SLIC3R_PP_OUTPUT_NAME", output_name.c_str(), 1); - std::string std_err; - const int result = run_script(script, gcode_file.string(), std_err); - if (result != 0) { - const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str() - : (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str(); - BOOST_LOG_TRIVIAL(error) << msg; - throw Slic3r::RuntimeError(msg); + // Path to an optional file that the post-processing script may create and populate it with a single line containing the output_name replacement. + std::string path_output_name = src_path + ".output_name"; + auto remove_output_name_file = [&path_output_name, &src_path]() { + try { + if (boost::filesystem::exists(path_output_name)) + boost::filesystem::remove(path_output_name); + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting a file %1% carrying the final name / path of a G-code file %2%: %3%", path_output_name, src_path, err.what()); + } + }; + // Remove possible stalled path_output_name of the previous run. + remove_output_name_file(); + + try { + for (const std::string &scripts : post_process->values) { + std::vector lines; + boost::split(lines, scripts, boost::is_any_of("\r\n")); + for (std::string script : lines) { + // Ignore empty post processing script lines. + boost::trim(script); + if (script.empty()) + continue; + BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path; + std::string std_err; + const int result = run_script(script, gcode_file.string(), std_err); + if (result != 0) { + const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str() + : (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str(); + BOOST_LOG_TRIVIAL(error) << msg; + delete_copy(); + throw Slic3r::RuntimeError(msg); + } } } + if (boost::filesystem::exists(path_output_name)) { + try { + // Read a single line from path_output_name, which should contain the new output name of the post-processed G-code. + boost::nowide::fstream f; + f.open(path_output_name, std::ios::in); + std::string new_output_name; + std::getline(f, new_output_name); + f.close(); + BOOST_LOG_TRIVIAL(trace) << "Post-processing script changed the file name from " << output_name << " to " << new_output_name; + output_name = new_output_name; + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed reading a file %1% carrying the final name / path of a G-code file: %2%", path_output_name, err.what()); + } + remove_output_name_file(); + } + } catch (...) { + remove_output_name_file(); + delete_copy(); + throw; } + + src_path = std::move(path); + return true; } } // namespace Slic3r diff --git a/src/libslic3r/GCode/PostProcessor.hpp b/src/libslic3r/GCode/PostProcessor.hpp index a9196aef7c..3ea4e0bc1e 100644 --- a/src/libslic3r/GCode/PostProcessor.hpp +++ b/src/libslic3r/GCode/PostProcessor.hpp @@ -8,7 +8,23 @@ namespace Slic3r { -extern void run_post_process_scripts(const std::string &path, const DynamicPrintConfig &config); +// Run post processing script / scripts if defined. +// Returns true if a post-processing script was executed. +// Returns false if no post-processing script was defined. +// Throws an exception on error. +// host is one of "File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ... +// If make_copy, then a temp file will be created for src_path by adding a ".pp" suffix and src_path will be updated. +// In that case the caller is responsible to delete the temp file created. +// output_name is the final name of the G-code on SD card or when uploaded to PrusaLink or OctoPrint. +// If uploading to PrusaLink or OctoPrint, then the file will be renamed to output_name first on the target host. +// The post-processing script may change the output_name. +extern bool run_post_process_scripts(std::string &src_path, bool make_copy, const std::string &host, std::string &output_name, const DynamicPrintConfig &config); + +inline bool run_post_process_scripts(std::string &src_path, const DynamicPrintConfig &config) +{ + std::string src_path_name = src_path; + return run_post_process_scripts(src_path, false, "File", src_path_name, config); +} } // namespace Slic3r diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index c48b8f2aaf..8a58e5ec93 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -154,54 +154,7 @@ void BackgroundSlicingProcess::process_fff() if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); - - // let the gcode window to unmap the temporary .gcode file (m_temp_output_path) - // because the scripts may want to modify it - GUI::wxGetApp().plater()->stop_mapping_gcode_window(); - - m_print->set_status(95, _utf8(L("Running post-processing scripts"))); - run_post_process_scripts(m_temp_output_path, m_fff_print->full_print_config()); - - // let the gcode window to reload and remap the temporary .gcode file (m_temp_output_path) - GUI::wxGetApp().plater()->start_mapping_gcode_window(); - - //FIXME localize the messages - // Perform the final post-processing of the export path by applying the print statistics over the file name. - std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); - std::string error_message; - int copy_ret_val = CopyFileResult::SUCCESS; - try - { - copy_ret_val = copy_file(m_temp_output_path, export_path, error_message, m_export_path_on_removable_media); - } - catch (...) - { - throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); - } - switch (copy_ret_val) { - case CopyFileResult::SUCCESS: break; // no error - case CopyFileResult::FAIL_COPY_FILE: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str()); - break; - case CopyFileResult::FAIL_FILES_DIFFERENT: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); - break; - case CopyFileResult::FAIL_RENAMING: - throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); - break; - case CopyFileResult::FAIL_CHECK_ORIGIN_NOT_OPENED: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str()); - break; - case CopyFileResult::FAIL_CHECK_TARGET_NOT_OPENED: - throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); - break; - default: - throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); - BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << "."; - break; - } - - m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); + finalize_gcode(); } else if (! m_upload_job.empty()) { wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id)); prepare_upload(); @@ -621,8 +574,11 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn // Some FFF status was invalidated, and the G-code was not exported yet. // Let the G-code preview UI know that the final G-code preview is not valid. // In addition, this early memory deallocation reduces memory footprint. - if (m_gcode_result != nullptr) + if (m_gcode_result != nullptr) { + //FIXME calling platter from here is not a staple of a good architecture. + GUI::wxGetApp().plater()->stop_mapping_gcode_window(); m_gcode_result->reset(); + } } return invalidated; } @@ -698,12 +654,73 @@ bool BackgroundSlicingProcess::invalidate_all_steps() return m_step_state.invalidate_all([this](){ this->stop_internal(); }); } +// G-code is generated in m_temp_output_path. +// Optionally run a post-processing script on a copy of m_temp_output_path. +// Copy the final G-code to target location (possibly a SD card, if it is a removable media, then verify that the file was written without an error). +void BackgroundSlicingProcess::finalize_gcode() +{ + m_print->set_status(95, _utf8(L("Running post-processing scripts"))); + + // Perform the final post-processing of the export path by applying the print statistics over the file name. + std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); + std::string output_path = m_temp_output_path; + // Both output_path and export_path ar in-out parameters. + // If post processed, output_path will differ from m_temp_output_path as run_post_process_scripts() will make a copy of the G-code to not + // collide with the G-code viewer memory mapping of the unprocessed G-code. G-code viewer maps unprocessed G-code, because m_gcode_result + // is calculated for the unprocessed G-code and it references lines in the memory mapped G-code file by line numbers. + // export_path may be changed by the post-processing script as well if the post processing script decides so, see GH #6042. + bool post_processed = run_post_process_scripts(output_path, true, "File", export_path, m_fff_print->full_print_config()); + auto remove_post_processed_temp_file = [post_processed, &output_path]() { + if (post_processed) + try { + boost::filesystem::remove(output_path); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to remove temp file " << output_path << ": " << ex.what(); + } + }; + + //FIXME localize the messages + std::string error_message; + int copy_ret_val = CopyFileResult::SUCCESS; + try + { + copy_ret_val = copy_file(output_path, export_path, error_message, m_export_path_on_removable_media); + remove_post_processed_temp_file(); + } + catch (...) + { + remove_post_processed_temp_file(); + throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); + } + switch (copy_ret_val) { + case CopyFileResult::SUCCESS: break; // no error + case CopyFileResult::FAIL_COPY_FILE: + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str()); + break; + case CopyFileResult::FAIL_FILES_DIFFERENT: + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str()); + break; + case CopyFileResult::FAIL_RENAMING: + throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str()); + break; + case CopyFileResult::FAIL_CHECK_ORIGIN_NOT_OPENED: + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % output_path % export_path).str()); + break; + case CopyFileResult::FAIL_CHECK_TARGET_NOT_OPENED: + throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str()); + break; + default: + throw Slic3r::ExportError(_utf8(L("Unknown error occured during exporting G-code."))); + BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << "."; + break; + } + + m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); +} + +// A print host upload job has been scheduled, enqueue it to the printhost job queue void BackgroundSlicingProcess::prepare_upload() { - // A print host upload job has been scheduled, enqueue it to the printhost job queue - - // XXX: is fs::path::string() right? - // Generate a unique temp path to which the gcode/zip file is copied/exported boost::filesystem::path source_path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%"); @@ -711,11 +728,15 @@ void BackgroundSlicingProcess::prepare_upload() if (m_print == m_fff_print) { m_print->set_status(95, _utf8(L("Running post-processing scripts"))); std::string error_message; - if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS) { + if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS) throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); - } - run_post_process_scripts(source_path.string(), m_fff_print->full_print_config()); m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); + // Make a copy of the source path, as run_post_process_scripts() is allowed to change it when making a copy of the source file + // (not here, but when the final target is a file). + std::string source_path_str = source_path.string(); + std::string output_name_str = m_upload_job.upload_data.upload_path.string(); + if (run_post_process_scripts(source_path_str, false, m_upload_job.printhost->get_name(), output_name_str, m_fff_print->full_print_config())) + m_upload_job.upload_data.upload_path = output_name_str; } else { m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index cf9b072495..f87a58fd6b 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -216,9 +216,9 @@ private: Print *m_fff_print = nullptr; SLAPrint *m_sla_print = nullptr; // Data structure, to which the G-code export writes its annotations. - GCodeProcessor::Result *m_gcode_result = nullptr; + GCodeProcessor::Result *m_gcode_result = nullptr; // Callback function, used to write thumbnails into gcode. - ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; + ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; SL1Archive m_sla_archive; // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; @@ -262,6 +262,7 @@ private: bool invalidate_all_steps(); // If the background processing stop was requested, throw CanceledException. void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); } + void finalize_gcode(); void prepare_upload(); // To be executed at the background thread. ThumbnailsList render_thumbnails(const ThumbnailsParams ¶ms); From 0e6d77d25a6f87595888037467fb7fb5a6f1f3e9 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 10 Aug 2021 15:25:25 +0200 Subject: [PATCH 063/240] Hints notifications: OS specific tags --- resources/data/hints.ini | 4 ++- src/slic3r/GUI/HintNotification.cpp | 41 +++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 0de06f04a7..6127fecc6d 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -43,7 +43,8 @@ # Each notification can have disabled and enabled modes and techs - divided by ; and space # enabled_tags = ... # disabled_tags = ... -# supported tags are: simple; advanced; expert; FFF; MMU; SLA +# supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX; +# Tags are case sensitive. # FFF is affirmative for both one or more extruder printers. # Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) # Algorithm shows hint only if not in all disabled tags. @@ -206,6 +207,7 @@ documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 [hint:Fullscreen mode] text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. +enabled_tags = Windows [hint:Simplify mesh] text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 973359bcbc..2f0f924493 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -68,6 +68,34 @@ inline TagCheckResult tag_check_tech(const std::string& tag) return TagCheckNotCompatible; } +inline TagCheckResult tag_check_system(const std::string& tag) +{ + std::vector allowed_tags = { "Windows", "Linux", "OSX" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { + if (tag =="Windows") +#ifdef WIN32 + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // WIN32 + + if (tag == "Linux") +#ifdef __linux__ + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // __linux__ + + if (tag == "OSX") +#ifdef __APPLE__ + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // __apple__ + } + return TagCheckNotCompatible; +} + // return true if NOT in disabled mode. inline bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) { @@ -93,6 +121,11 @@ inline bool tags_check(const std::string& disabled_tags, const std::string& enab if (result == TagCheckResult::TagCheckAffirmative) continue; result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_system(tag); if (result == TagCheckResult::TagCheckNegative) return false; if (result == TagCheckResult::TagCheckAffirmative) @@ -128,6 +161,11 @@ inline bool tags_check(const std::string& disabled_tags, const std::string& enab continue; if (result == TagCheckResult::TagCheckAffirmative) return false; + result = tag_check_system(tag); + if (result == TagCheckResult::TagCheckAffirmative) + return false; + if (result == TagCheckResult::TagCheckNegative) + continue; BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in disabled_tags not compatible."; } } @@ -655,8 +693,7 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp //hover if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 15.f, win_pos_y + m_window_height - 1.75f * m_line_height), ImVec2(win_pos_x, win_pos_y + m_window_height), - true)) - { + true)) { button_text = ImGui::PreferencesHoverButton; // tooltip long time_now = wxGetLocalTime(); From e5ce748b1092d96a154f7e46d922f945738fd383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 10 Aug 2021 21:37:10 +0200 Subject: [PATCH 064/240] Added missing includes (GCC 11.1) --- src/libslic3r/GCode/PostProcessor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 2ffffc7d6d..7afd3fa913 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -1,12 +1,15 @@ #include "PostProcessor.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/format.hpp" #include #include #include #include #include +#include +#include #ifdef WIN32 From 18cff61bd251d9290084965ac4d2db7608700c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 10 Aug 2021 21:38:48 +0200 Subject: [PATCH 065/240] Reworked visualization of selected triangles by seed fill and bucket fill inside the multi-material painting gizmo. Now is drawn boundary around a selected area, and also all triangles inside the selected area are drawn with a darker color. --- resources/shaders/mm_contour.fs | 6 + resources/shaders/mm_contour.vs | 6 + src/libslic3r/TriangleSelector.cpp | 83 +++++++++++- src/libslic3r/TriangleSelector.hpp | 10 +- src/slic3r/GUI/GLShadersManager.cpp | 2 + .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 124 +++++++++++++++--- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 25 +++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 17 ++- 8 files changed, 239 insertions(+), 34 deletions(-) create mode 100644 resources/shaders/mm_contour.fs create mode 100644 resources/shaders/mm_contour.vs diff --git a/resources/shaders/mm_contour.fs b/resources/shaders/mm_contour.fs new file mode 100644 index 0000000000..14c18dcf16 --- /dev/null +++ b/resources/shaders/mm_contour.fs @@ -0,0 +1,6 @@ +#version 110 + +void main() +{ + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/resources/shaders/mm_contour.vs b/resources/shaders/mm_contour.vs new file mode 100644 index 0000000000..d0d3ee42a9 --- /dev/null +++ b/resources/shaders/mm_contour.vs @@ -0,0 +1,6 @@ +#version 110 + +void main() +{ + gl_Position = ftransform(); +} diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 9e16c03178..485a7fcf24 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -295,11 +295,48 @@ void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi, process_subtriangle(touching.second, Partition::Second); } -void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate) +// It appends all edges that are touching the edge (vertexi, vertexj) of the triangle and are not selected by seed fill +// It doesn't append the edges that are touching the triangle only by part of the edge that means the triangles are from lower depth. +void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int vertexj, std::vector &touching_edges_out) const +{ + if (itriangle == -1) + return; + + auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_edges_out](const int subtriangle_idx, Partition partition) -> void { + assert(subtriangle_idx != -1); + if (!m_triangles[subtriangle_idx].is_split()) { + if (!m_triangles[subtriangle_idx].is_selected_by_seed_fill()) { + int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); + if (partition == Partition::First && midpoint != -1) { + touching_edges_out.emplace_back(vertexi, midpoint); + } else if (partition == Partition::First && midpoint == -1) { + touching_edges_out.emplace_back(vertexi, vertexj); + } else { + assert(midpoint != -1 && partition == Partition::Second); + touching_edges_out.emplace_back(midpoint, vertexj); + } + } + } else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1) + append_touching_edges(subtriangle_idx, partition == Partition::First ? vertexi : midpoint, partition == Partition::First ? midpoint : vertexj, + touching_edges_out); + else + append_touching_edges(subtriangle_idx, vertexi, vertexj, touching_edges_out); + }; + + std::pair touching = this->triangle_subtriangles(itriangle, vertexi, vertexj); + if (touching.first != -1) + process_subtriangle(touching.first, Partition::First); + + if (touching.second != -1) + process_subtriangle(touching.second, Partition::Second); +} + +void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate, bool force_reselection) { int start_facet_idx = select_unsplit_triangle(hit, facet_start); + assert(start_facet_idx != -1); // Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill. - if (start_facet_idx == -1 || m_triangles[start_facet_idx].is_selected_by_seed_fill()) + if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection)) return; assert(!m_triangles[start_facet_idx].is_split()); @@ -1358,6 +1395,48 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const } } +std::vector TriangleSelector::get_seed_fill_contour() const { + std::vector edges_out; + for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) { + const Vec3i neighbors = root_neighbors(*m_mesh, facet_idx); + assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors)); + this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out); + } + + return edges_out; +} + +void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &edges_out) const { + assert(facet_idx != -1 && facet_idx < int(m_triangles.size())); + assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors)); + const Triangle *tr = &m_triangles[facet_idx]; + if (!tr->valid()) + return; + + if (tr->is_split()) { + int num_of_children = tr->number_of_split_sides() + 1; + if (num_of_children != 1) { + for (int i = 0; i < num_of_children; ++i) { + assert(i < int(tr->children.size())); + assert(tr->children[i] < int(m_triangles.size())); + // Recursion, deep first search over the children of this triangle. + // All children of this triangle were created by splitting a single source triangle of the original mesh. + this->get_seed_fill_contour_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), this->child_neighbors_propagated(*tr, neighbors_propagated, i), edges_out); + } + } + } else if (tr->is_selected_by_seed_fill()) { + Vec3i vertices = {m_triangles[facet_idx].verts_idxs[0], m_triangles[facet_idx].verts_idxs[1], m_triangles[facet_idx].verts_idxs[2]}; + append_touching_edges(neighbors(0), vertices(1), vertices(0), edges_out); + append_touching_edges(neighbors(1), vertices(2), vertices(1), edges_out); + append_touching_edges(neighbors(2), vertices(0), vertices(2), edges_out); + + // It appends the edges that are touching the triangle only by part of the edge that means the triangles are from lower depth. + for (int idx = 0; idx < 3; ++idx) + if (int neighbor_tr_idx = neighbors_propagated(idx); neighbor_tr_idx != -1 && !m_triangles[neighbor_tr_idx].is_split() && !m_triangles[neighbor_tr_idx].is_selected_by_seed_fill()) + edges_out.emplace_back(vertices(idx), vertices(next_idx_modulo(idx, 3))); + } +} + std::pair>, std::vector> TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 39c1ad22e3..2ab0531235 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -53,8 +53,9 @@ public: bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle void bucket_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - bool propagate); // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool has_facets(EnforcerBlockerType state) const; static bool has_facets(const std::pair>, std::vector> &data, EnforcerBlockerType test_state); @@ -63,6 +64,8 @@ public: indexed_triangle_set get_facets(EnforcerBlockerType state) const; // Get facets at a given state. Triangulate T-joints. indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const; + // Get edges around the selected area by seed fill. + std::vector get_seed_fill_contour() const; // Set facet of the mesh to a given state. Only works for original triangles. void set_facet(int facet_idx, EnforcerBlockerType state); @@ -222,6 +225,7 @@ private: std::pair triangle_subtriangles(int itriangle, int vertexi, int vertexj) const; void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector &touching_subtriangles_out) const; + void append_touching_edges(int itriangle, int vertexi, int vertexj, std::vector &touching_edges_out) const; #ifndef NDEBUG bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const; @@ -235,6 +239,8 @@ private: std::vector &out_triangles) const; void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector &out_triangles) const; + void get_seed_fill_contour_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &edges_out) const; + int m_free_triangles_head { -1 }; int m_free_vertices_head { -1 }; }; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 788fe90c0c..33eec63e8b 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -61,6 +61,8 @@ std::pair GLShadersManager::init() ); // used to render variable layers heights in 3d editor valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); + // used to render highlight contour around selected triangles inside the multi-material gizmo + valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" }); return { valid, error }; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index c992813593..3d8be9eff0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -336,8 +336,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott } } - - if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(max_tooltip_width); @@ -467,7 +465,12 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott "placed after the number with no whitespace in between."); ImGui::SameLine(sliders_width); ImGui::PushItemWidth(window_width - sliders_width); - m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, SeedFillAngleMin, SeedFillAngleMax, format_str.data()); + if(m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, SeedFillAngleMin, SeedFillAngleMax, format_str.data())) + for (auto &triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(max_tooltip_width); @@ -594,10 +597,13 @@ std::array GLGizmoMmuSegmentation::get_cursor_sphere_right_button_colo return {color[0], color[1], color[2], 0.25f}; } +static std::array get_seed_fill_color(const std::array &base_color) +{ + return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f}; +} + void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) { - static constexpr std::array seed_fill_color{0.f, 1.f, 0.44f, 1.f}; - if (m_update_render_data) update_render_data(); @@ -610,12 +616,24 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx) if (m_gizmo_scene.has_VBOs(color_idx)) { - shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : - color_idx == (m_gizmo_scene.triangle_indices.size() - 1) ? seed_fill_color : - m_colors[color_idx - 1]); + if (color_idx > m_colors.size()) // Seed fill VBO + shader->set_uniform("uniform_color", get_seed_fill_color(color_idx == (m_colors.size() + 1) ? m_default_volume_color : m_colors[color_idx - (m_colors.size() + 1) - 1])); + else // Normal VBO + shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : m_colors[color_idx - 1]); + m_gizmo_scene.render(color_idx); } + if (m_gizmo_scene.has_contour_VBO()) { + ScopeGuard guard_gouraud([shader]() { shader->start_using(); }); + shader->stop_using(); + + auto *contour_shader = wxGetApp().get_shader("mm_contour"); + contour_shader->start_using(); + m_gizmo_scene.render_contour(); + contour_shader->stop_using(); + } + m_update_render_data = false; } @@ -632,10 +650,10 @@ void TriangleSelectorMmGui::update_render_data() for (const Triangle &tr : m_triangles) if (tr.valid() && !tr.is_split()) { - int color = int(tr.get_state()); - std::vector &iva = tr.is_selected_by_seed_fill() ? m_gizmo_scene.triangle_indices.back() : - color < int(m_gizmo_scene.triangle_indices.size() - 1) ? m_gizmo_scene.triangle_indices[color] : - m_gizmo_scene.triangle_indices.front(); + int color = int(tr.get_state()) <= int(m_colors.size()) ? int(tr.get_state()) : 0; + assert(m_colors.size() + 1 + color < m_gizmo_scene.triangle_indices.size()); + std::vector &iva = m_gizmo_scene.triangle_indices[color + tr.is_selected_by_seed_fill() * (m_colors.size() + 1)]; + if (iva.size() + 3 > iva.capacity()) iva.reserve(next_highest_power_of_2(iva.size() + 3)); @@ -648,6 +666,24 @@ void TriangleSelectorMmGui::update_render_data() m_gizmo_scene.triangle_indices_sizes[color_idx] = m_gizmo_scene.triangle_indices[color_idx].size(); m_gizmo_scene.finalize_triangle_indices(); + + std::vector contour_edges = this->get_seed_fill_contour(); + m_gizmo_scene.contour_vertices.reserve(contour_edges.size() * 6); + for (const Vec2i &edge : contour_edges) { + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.x()); + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.y()); + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.z()); + + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.x()); + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.y()); + m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.z()); + } + + m_gizmo_scene.contour_indices.assign(m_gizmo_scene.contour_vertices.size() / 3, 0); + std::iota(m_gizmo_scene.contour_indices.begin(), m_gizmo_scene.contour_indices.end(), 0); + m_gizmo_scene.contour_indices_size = m_gizmo_scene.contour_indices.size(); + + m_gizmo_scene.finalize_contour(); } wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const @@ -671,6 +707,14 @@ void GLMmSegmentationGizmo3DScene::release_geometry() { glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id)); triangle_indices_VBO_id = 0; } + if (this->contour_vertices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->contour_vertices_VBO_id)); + this->contour_vertices_VBO_id = 0; + } + if (this->contour_indices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->contour_indices_VBO_id)); + this->contour_indices_VBO_id = 0; + } this->clear(); } @@ -681,6 +725,10 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const assert(this->vertices_VBO_id != 0); assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0); + ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); }); + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(5.0, 5.0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id)); glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float)))); @@ -698,13 +746,36 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } +void GLMmSegmentationGizmo3DScene::render_contour() const +{ + assert(this->contour_vertices_VBO_id != 0); + assert(this->contour_indices_VBO_id != 0); + + glsafe(::glLineWidth(4.0f)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + if (this->contour_indices_size > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices_VBO_id)); + glsafe(::glDrawElements(GL_LINES, GLsizei(this->contour_indices_size), GL_UNSIGNED_INT, nullptr)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + void GLMmSegmentationGizmo3DScene::finalize_vertices() { assert(this->vertices_VBO_id == 0); if (!this->vertices.empty()) { glsafe(::glGenBuffers(1, &this->vertices_VBO_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * 4, this->vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(float), this->vertices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); this->vertices.clear(); } @@ -719,19 +790,32 @@ void GLMmSegmentationGizmo3DScene::finalize_triangle_indices() if (!this->triangle_indices[buffer_idx].empty()) { glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_ids[buffer_idx])); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[buffer_idx])); - glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices[buffer_idx].size() * 4, this->triangle_indices[buffer_idx].data(), - GL_STATIC_DRAW)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices[buffer_idx].size() * sizeof(int), this->triangle_indices[buffer_idx].data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); this->triangle_indices[buffer_idx].clear(); } } -void GLMmSegmentationGizmo3DScene::finalize_geometry() +void GLMmSegmentationGizmo3DScene::finalize_contour() { - assert(this->vertices_VBO_id == 0); - assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size()); - finalize_vertices(); - finalize_triangle_indices(); + assert(this->contour_vertices_VBO_id == 0); + assert(this->contour_indices_VBO_id == 0); + + if (!this->contour_vertices.empty()) { + glsafe(::glGenBuffers(1, &this->contour_vertices_VBO_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_vertices.size() * sizeof(float), this->contour_vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + this->contour_vertices.clear(); + } + + if (!this->contour_indices.empty()) { + glsafe(::glGenBuffers(1, &this->contour_indices_VBO_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_indices_VBO_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + this->contour_indices.clear(); + } } } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index b1b19bfaca..afd5854a09 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -25,9 +25,8 @@ public: return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0; } - // Finalize the initialization of the geometry and indices, upload the geometry and indices to OpenGL VBO objects - // and possibly releasing it if it has been loaded into the VBOs. - void finalize_geometry(); + [[nodiscard]] inline bool has_contour_VBO() const { return this->contour_indices_VBO_id != 0; } + // Release the geometry data, release OpenGL VBOs. void release_geometry(); // Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects @@ -36,6 +35,9 @@ public: // Finalize the initialization of the indices, upload the indices to OpenGL VBO objects // and possibly releasing it if it has been loaded into the VBOs. void finalize_triangle_indices(); + // Finalize the initialization of the contour geometry and the indices, upload both to OpenGL VBO objects + // and possibly releasing it if it has been loaded into the VBOs. + void finalize_contour(); void clear() { @@ -45,28 +47,41 @@ public: for (size_t &triangle_indices_size : this->triangle_indices_sizes) triangle_indices_size = 0; + + this->contour_vertices.clear(); + this->contour_indices.clear(); + this->contour_indices_size = 0; } void render(size_t triangle_indices_idx) const; + void render_contour() const; + std::vector vertices; std::vector> triangle_indices; + std::vector contour_vertices; + std::vector contour_indices; + // When the triangle indices are loaded into the graphics card as Vertex Buffer Objects, // the above mentioned std::vectors are cleared and the following variables keep their original length. std::vector triangle_indices_sizes; + size_t contour_indices_size{0}; // IDs of the Vertex Array Objects, into which the geometry has been loaded. // Zero if the VBOs are not sent to GPU yet. unsigned int vertices_VBO_id{0}; std::vector triangle_indices_VBO_ids; + + unsigned int contour_vertices_VBO_id{0}; + unsigned int contour_indices_VBO_id{0}; }; class TriangleSelectorMmGui : public TriangleSelectorGUI { public: - // Plus 2 in the initialization of m_gizmo_scene is because the first position is allocated for non-painted triangles, and the last position is allocated for seed fill. + // Plus 1 in the initialization of m_gizmo_scene is because the first position is allocated for non-painted triangles, and the indices above colors.size() are allocated for seed fill. explicit TriangleSelectorMmGui(const TriangleMesh &mesh, const std::vector> &colors, const std::array &default_volume_color) - : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color), m_gizmo_scene(colors.size() + 2) {} + : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color), m_gizmo_scene(2 * (colors.size() + 1)) {} ~TriangleSelectorMmGui() override = default; // Render current selection. Transformation matrices are supposed diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 2e8c5a4a76..ac9d7adcf1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -324,11 +324,11 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous new_state = action == SLAGizmoEventType::LeftDown ? this->get_left_button_state_type() : this->get_right_button_state_type(); } - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + const Selection &selection = m_parent.get_selection(); + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + const Transform3d &instance_trafo = mi->get_transformation().get_matrix(); // List of mouse positions that will be used as seeds for painting. std::vector mouse_positions{mouse_position}; @@ -387,6 +387,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous assert(m_rr.mesh_id < int(m_triangle_selectors.size())); if (m_tool_type == ToolType::SEED_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) { m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); + if (m_tool_type == ToolType::SEED_FILL) + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle, true); + else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true); + else if (m_tool_type == ToolType::BUCKET_FILL) + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true, true); + m_seed_fill_last_mesh_id = -1; } else if (m_tool_type == ToolType::BRUSH) m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, From d735dbe147e6c16d6a2c071622db81b3d0d1d332 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 11 Aug 2021 09:23:51 +0200 Subject: [PATCH 066/240] Updated Items Info notification Showing only when new object is added (typically loading projects). All info in 1 notification. --- src/slic3r/GUI/GUI_ObjectList.cpp | 8 ++-- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- src/slic3r/GUI/NotificationManager.cpp | 54 +++++++++++++++++++------- src/slic3r/GUI/NotificationManager.hpp | 5 ++- 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 2f86f998b7..05e9f252c9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2517,7 +2517,7 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D } -void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selections/* = nullptr*/) +void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selections/* = nullptr*/, bool added_object/* = false*/) { const ModelObject* model_object = (*m_objects)[obj_idx]; wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); @@ -2561,8 +2561,8 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio if (! shows && should_show) { m_objects_model->AddInfoChild(item_obj, type); Expand(item_obj); - wxGetApp().notification_manager()->push_updated_item_info_notification(type); - + if (added_object) + wxGetApp().notification_manager()->push_updated_item_info_notification(type); } else if (shows && ! should_show) { if (!selections) @@ -2594,7 +2594,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) model_object->config.has("extruder") ? model_object->config.extruder() : 0, get_mesh_errors_count(obj_idx) > 0); - update_info_items(obj_idx); + update_info_items(obj_idx, nullptr, true); // add volumes to the object if (model_object->volumes.size() > 1) { diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b41628a258..71730b2c06 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -350,7 +350,7 @@ public: void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); - void update_info_items(size_t obj_idx, wxDataViewItemArray* selections = nullptr); + void update_info_items(size_t obj_idx, wxDataViewItemArray* selections = nullptr, bool added_object = false); void instances_to_separated_object(const int obj_idx, const std::set& inst_idx); void instances_to_separated_objects(const int obj_idx); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 5b88ad9e7c..01b0144417 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -61,7 +61,7 @@ static const std::map font_icons_large = { {ImGui::SinkingObjectMarker , "move" }, {ImGui::CustomSupportsMarker , "fdm_supports" }, {ImGui::CustomSeamMarker , "seam" }, - {ImGui::MmuSegmentationMarker , "move" }, + {ImGui::MmuSegmentationMarker , "fdm_supports" }, {ImGui::VarLayerHeightMarker , "layers" }, {ImGui::DocumentationButton , "notification_documentation" }, {ImGui::DocumentationHoverButton, "notification_documentation_hover"}, diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 9620f803e5..84f31a03f8 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1074,10 +1074,39 @@ void NotificationManager::UpdatedItemsInfoNotification::count_spaces() m_window_width_offset = m_left_indentation + m_line_height * 3.f; m_window_width = m_line_height * 25; } +void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType type) +{ + std::vector>::iterator it = m_types_and_counts.begin(); + for (; it != m_types_and_counts.end(); ++it) { + if ((*it).first == type) { + (*it).second++; + break; + } + } + if (it == m_types_and_counts.end()) + m_types_and_counts.emplace_back(type, 1); + + std::string text; + for (it = m_types_and_counts.begin(); it != m_types_and_counts.end(); ++it) { + text += std::to_string((*it).second); + text += _L_PLURAL(" Object was loaded with "," Objects were loaded with ", (*it).second).ToUTF8().data(); + switch ((*it).first) { + case InfoItemType::CustomSupports: text += _utf8("custom supports.\n"); break; + case InfoItemType::CustomSeam: text += _utf8("custom seam.\n"); break; + case InfoItemType::MmuSegmentation: text += _utf8("multimaterial painting.\n"); break; + case InfoItemType::VariableLayerHeight: text += _utf8("variable layer height.\n"); break; + case InfoItemType::Sinking: text += _utf8("Partial sinking.\n"); break; + default: text.clear(); break; + } + } + NotificationData data { get_data().type, get_data().level , get_data().duration, text }; + update(data); +} void NotificationManager::UpdatedItemsInfoNotification::render_left_sign(ImGuiWrapper& imgui) { std::string text; - switch (m_info_item_type) { + InfoItemType type = (m_types_and_counts.empty() ? InfoItemType::CustomSupports : m_types_and_counts[0].first); + switch (type) { case InfoItemType::CustomSupports: text = ImGui::CustomSupportsMarker; break; case InfoItemType::CustomSeam: text = ImGui::CustomSeamMarker; break; case InfoItemType::MmuSegmentation: text = ImGui::MmuSegmentationMarker; break; @@ -1349,7 +1378,6 @@ void NotificationManager::upload_job_notification_show_error(int id, const std:: } void NotificationManager::push_hint_notification(bool open_next) { - for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::DidYouKnowHint) { if (open_next) @@ -1375,19 +1403,19 @@ bool NotificationManager::is_hint_notification_open() void NotificationManager::push_updated_item_info_notification(InfoItemType type) { - std::string text = _utf8("Object(s) were loaded with "); - switch (type) { - case InfoItemType::CustomSupports: text += _utf8("custom supports."); break; - case InfoItemType::CustomSeam: text += _utf8("custom seam."); break; - case InfoItemType::MmuSegmentation: text += _utf8("multimaterial painting."); break; - case InfoItemType::VariableLayerHeight: text += _utf8("variable layer height."); break; - case InfoItemType::Sinking: text = _utf8("Partially sinking object(s) were loaded."); break; - default: text.clear(); break; + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::UpdatedItemsInfo) { + (dynamic_cast(notification.get()))->add_type(type); + return; + } } - if (!text.empty()) { - NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotification, 10, text }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, type), 0); + + NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotification, 5, "" }; + auto notification = std::make_unique(data, m_id_provider, m_evt_handler, type); + if (push_notification_data(std::move(notification), 0)) { + (dynamic_cast(m_pop_notifications.back().get()))->add_type(type); } + } bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 72e8677048..b347c9dfe4 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -499,13 +499,14 @@ private: public: UpdatedItemsInfoNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, InfoItemType info_item_type) : PopNotification(n, id_provider, evt_handler) - , m_info_item_type(info_item_type) { + //m_types_and_counts.emplace_back(info_item_type, 1); } void count_spaces() override; + void add_type(InfoItemType type); protected: void render_left_sign(ImGuiWrapper& imgui) override; - InfoItemType m_info_item_type; + std::vector> m_types_and_counts; }; // in HintNotification.hpp From 5b6f2ed061323173524d34dcbb07fa90ca7103ca Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 11 Aug 2021 09:29:47 +0200 Subject: [PATCH 067/240] Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height). --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 88 ++++++++++++++++++---------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index d4d05f625b..a0a7c1e2d6 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.4.0-alpha0 +1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height). 1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). 1.4.0-alpha4 Decreased Area Fill (SL1S). 1.4.0-alpha3 Updated SL1S tilt times. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 0f3fa63648..80095f4abc 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.4.0-alpha5 +config_version = 1.4.0-alpha6 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -488,7 +488,7 @@ perimeter_acceleration = 800 perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 -top_solid_layers = 5 +top_solid_layers = 6 [print:*0.25mm*] inherits = *common* @@ -5832,6 +5832,7 @@ printer_model = MK2S printer_variant = 0.4 default_print_profile = 0.15mm OPTIMAL default_filament_profile = Prusament PLA +color_change_gcode = M600\nG1 E0.4 F1500 ; prime after color change [printer:*multimaterial*] inherits = *common* @@ -5888,6 +5889,7 @@ variable_layer_height = 1 printer_variant = 0.25 retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle +color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK2S 0.6 nozzle] inherits = *common* @@ -5896,6 +5898,7 @@ min_layer_height = 0.1 nozzle_diameter = 0.6 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change # XXXXXXXXXXXXXXXXXXX # XXX--- MK2MM ---XXX @@ -5937,21 +5940,21 @@ inherits = Original Prusa i3 MK2S printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.6 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.8 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -5963,9 +5966,10 @@ min_layer_height = 0.2 retract_length = 1 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5 MMU2 Single] inherits = *25mm2* @@ -5975,7 +5979,7 @@ max_print_height = 200 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; load to nozzle\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.20 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; load to nozzle\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.20 F1000\nG1 X5 E4 F1000\nG92 E0\n end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5 MMU2 Single 0.8 nozzle] @@ -5999,7 +6003,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in single_extruder_multi_material = 1 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S] @@ -6026,7 +6030,7 @@ max_print_height = 200 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle] @@ -6037,9 +6041,10 @@ min_layer_height = 0.2 nozzle_diameter = 0.8 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5S MMU2S Single 0.6 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -6049,6 +6054,7 @@ min_layer_height = 0.1 nozzle_diameter = 0.6 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5S MMU2S Single 0.25 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -6059,7 +6065,8 @@ nozzle_diameter = 0.25 printer_variant = 0.25 retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5S MMU2S] inherits = *25mm2s* @@ -6070,7 +6077,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in single_extruder_multi_material = 1 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] @@ -6080,6 +6087,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK2.5 MMU2 0.6 nozzle] inherits = Original Prusa i3 MK2.5 MMU2 @@ -6088,6 +6096,7 @@ max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change ## 0.8mm nozzle profiles are only available for MMU2 Single mode at the moment. @@ -6099,7 +6108,7 @@ default_print_profile = 0.20mm NORMAL @0.6 nozzle ## printer_variant = 0.8 ## retract_length = 1 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n ## [printer:Original Prusa i3 MK2.5 MMU2 0.8 nozzle] ## inherits = Original Prusa i3 MK2.5 MMU2 @@ -6109,7 +6118,7 @@ default_print_profile = 0.20mm NORMAL @0.6 nozzle ## printer_variant = 0.8 ## retract_length = 1 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n # XXXXXXXXXXXXXXXXX # XXX--- MK3 ---XXX @@ -6139,7 +6148,7 @@ remaining_times = 1 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n retract_lift_below = 209 max_print_height = 210 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} printer_model = MK3 default_print_profile = 0.15mm QUALITY @MK3 @@ -6150,8 +6159,9 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 +color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK3 0.6 nozzle] inherits = Original Prusa i3 MK3 @@ -6159,8 +6169,9 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK3 0.8 nozzle] inherits = Original Prusa i3 MK3 @@ -6169,9 +6180,10 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S95 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S95 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa i3 MK3S & MK3S+] inherits = Original Prusa i3 MK3 @@ -6240,7 +6252,7 @@ default_filament_profile = Prusament PLA @MMU2 inherits = *mm2* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] @@ -6250,8 +6262,9 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK3 MMU2 Single 0.8 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single 0.6 nozzle @@ -6261,9 +6274,10 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa i3 MK3 MMU2 Single 0.25 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single @@ -6273,15 +6287,16 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 +color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK3 MMU2] inherits = *mm2* machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single] @@ -6289,7 +6304,7 @@ inherits = *mm2s* renamed_from = "Original Prusa i3 MK3S MMU2S Single" single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle] @@ -6300,8 +6315,9 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.8 nozzle] inherits = Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle @@ -6311,9 +6327,10 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.25 nozzle] inherits = Original Prusa i3 MK3S & MK3S+ MMU2S Single @@ -6324,8 +6341,9 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 +color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK3S & MK3S+ MMU2S] inherits = *mm2s* @@ -6333,7 +6351,7 @@ renamed_from = "Original Prusa i3 MK3S MMU2S" machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n ## 0.6mm nozzle MMU2/S printer profiles @@ -6345,8 +6363,9 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change [printer:Original Prusa i3 MK3 MMU2 0.6 nozzle] inherits = Original Prusa i3 MK3 MMU2 @@ -6354,8 +6373,9 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 +color_change_gcode = M600\nG1 E0.5 F1500 ; prime after color change ## 0.8mm nozzle MMU2/S printer profiles @@ -6367,7 +6387,7 @@ default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## [printer:Original Prusa i3 MK3S & MK3S+ MMU2S 0.8 nozzle] @@ -6376,7 +6396,7 @@ default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.10.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## MINI @@ -6430,6 +6450,7 @@ start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} F720 ; Move print head further up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n extruder_colour = +color_change_gcode = M600\nG1 E0.8 F1500 ; prime after color change [printer:Original Prusa MINI & MINI+ 0.25 nozzle] inherits = Original Prusa MINI & MINI+ @@ -6443,6 +6464,7 @@ retract_length = 3 retract_lift = 0.15 retract_before_travel = 1 start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F600\nG1 X40 E10 F400\nG92 E0\n\nM221 S95 ; set flow +color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change [printer:Original Prusa MINI & MINI+ 0.6 nozzle] inherits = Original Prusa MINI & MINI+ @@ -6454,6 +6476,7 @@ min_layer_height = 0.15 default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI retract_length = 3.5 retract_before_travel = 1.5 +color_change_gcode = M600\nG1 E1 F1500 ; prime after color change [printer:Original Prusa MINI & MINI+ 0.8 nozzle] inherits = Original Prusa MINI & MINI+ @@ -6465,6 +6488,7 @@ default_print_profile = 0.40mm QUALITY @0.8 nozzle MINI default_filament_profile = Prusament PLA @0.8 nozzle retract_length = 3.5 retract_before_travel = 1.5 +color_change_gcode = M600\nG1 E1.2 F1500 ; prime after color change [printer:Original Prusa SL1] printer_technology = SLA From 7a60e8cb3aab8323a1d08585efea7c6a70f35ef9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 11 Aug 2021 09:49:13 +0200 Subject: [PATCH 068/240] Follow-up to 215ee293ae705943e28ab1509f50d03c85dd26a0: More robust CLI parser. --- src/libslic3r/Config.cpp | 92 +++++++++++++++------------------------- src/libslic3r/Config.hpp | 1 - 2 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index cbc434b395..8fb774cb92 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -223,10 +223,13 @@ std::vector ConfigOptionDef::cli_args(const std::string &key) const { std::vector args; if (this->cli != ConfigOptionDef::nocli) { - std::string cli = this->cli.substr(0, this->cli.find("=")); - boost::trim_right_if(cli, boost::is_any_of("!")); + const std::string &cli = this->cli; + //FIXME What was that for? Check the "readline" documentation. + // Neither '=' nor '!' is used in any of the cli parameters currently defined by PrusaSlicer. +// std::string cli = this->cli.substr(0, this->cli.find("=")); +// boost::trim_right_if(cli, boost::is_any_of("!")); if (cli.empty()) { - // Add the key + // Convert an option key to CLI argument by replacing underscores with dashes. std::string opt = key; boost::replace_all(opt, "_", "-"); args.emplace_back(std::move(opt)); @@ -817,22 +820,12 @@ const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) co return (it == options.end()) ? nullptr : it->second.get(); } -void DynamicConfig::read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys) -{ - std::vector args; - // push a bogus executable name (argv[0]) - args.emplace_back(""); - for (size_t i = 0; i < tokens.size(); ++ i) - args.emplace_back(tokens[i].c_str()); - this->read_cli(int(args.size()), args.data(), extra, keys); -} - bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys) { // cache the CLI option => opt_key mapping std::map opts; for (const auto &oit : this->def()->options) - for (auto t : oit.second.cli_args(oit.first)) + for (const std::string &t : oit.second.cli_args(oit.first)) opts[t] = oit.first; bool parse_options = true; @@ -854,14 +847,8 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option parse_options = false; continue; } - // Remove leading dashes - boost::trim_left_if(token, boost::is_any_of("-")); - // Remove the "no-" prefix used to negate boolean options. - bool no = false; - if (boost::starts_with(token, "no-")) { - no = true; - boost::replace_first(token, "no-", ""); - } + // Remove leading dashes (one or two). + token.erase(token.begin(), token.begin() + (boost::starts_with(token, "--") ? 2 : 1)); // Read value when supplied in the --key=value form. std::string value; { @@ -871,54 +858,45 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option token.erase(equals_pos); } } - // Look for the cli -> option mapping. - const auto it = opts.find(token); + auto it = opts.find(token); + bool no = false; if (it == opts.end()) { - boost::nowide::cerr << "Unknown option --" << token.c_str() << std::endl; - return false; + // Remove the "no-" prefix used to negate boolean options. + std::string yes_token; + if (boost::starts_with(token, "no-")) { + yes_token = token.substr(3); + it = opts.find(yes_token); + no = true; + } + if (it == opts.end()) { + boost::nowide::cerr << "Unknown option --" << token.c_str() << std::endl; + return false; + } + if (no) + token = yes_token; } - const t_config_option_key opt_key = it->second; - const ConfigOptionDef &optdef = this->def()->options.at(opt_key); + + const t_config_option_key &opt_key = it->second; + const ConfigOptionDef &optdef = this->def()->options.at(opt_key); // If the option type expects a value and it was not already provided, // look for it in the next token. - if (value.empty()) { - if (optdef.type != coBool && optdef.type != coBools) { - if (i == (argc-1)) { - boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; - return false; - } - value = argv[++ i]; - } else { - // This is a bool or bools. The value is optional, but may still be there. - // Check if the next token can be deserialized into ConfigOptionBool. - // If it is in fact bools, it will be rejected later anyway. - if (i != argc-1) { // There is still a token to read. - ConfigOptionBool cobool; - if (cobool.deserialize(argv[i+1])) - value = argv[++i]; - } + if (value.empty() && optdef.type != coBool && optdef.type != coBools) { + if (i == argc-1) { + boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl; + return false; } - + value = argv[++ i]; } if (no) { - if (optdef.type != coBool && optdef.type != coBools) { - boost::nowide::cerr << "Only boolean config options can be negated with --no- prefix." << std::endl; - return false; - } - else if (! value.empty()) { + assert(optdef.type == coBool || optdef.type == coBools); + if (! value.empty()) { boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl; return false; } } - if (optdef.type == coBools && ! value.empty()) { - boost::nowide::cerr << "Vector boolean options cannot have a value. Fill them in by " - "repeating them and negate by --no- prefix." << std::endl; - return false; - } - // Store the option value. const bool existing = this->has(opt_key); @@ -934,7 +912,7 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option opt_vector->clear(); // Vector values will be chained. Repeated use of a parameter will append the parameter or parameters // to the end of the value. - if (opt_base->type() == coBools) + if (opt_base->type() == coBools && value.empty()) static_cast(opt_base)->values.push_back(!no); else // Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index bf356dfe65..04355bd277 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2099,7 +2099,6 @@ public: bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } // Command line processing - void read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); bool read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys = nullptr); std::map>::const_iterator cbegin() const { return options.cbegin(); } From 49ead341ba7524f66522c1b8a13d5f00353da1b6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 12:08:39 +0200 Subject: [PATCH 069/240] Follow-up of cf32b56 (postprocessing scripts): - The optional output_name file might contain the .pp suffix. - In case the file contains just filename, prepend it with the output dir. We don't want to save to current workdir. --- src/libslic3r/GCode/PostProcessor.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 7afd3fa913..453fddaa3e 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -247,7 +247,7 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: boost::nowide::setenv("SLIC3R_PP_OUTPUT_NAME", output_name.c_str(), 1); // Path to an optional file that the post-processing script may create and populate it with a single line containing the output_name replacement. - std::string path_output_name = src_path + ".output_name"; + std::string path_output_name = path + ".output_name"; auto remove_output_name_file = [&path_output_name, &src_path]() { try { if (boost::filesystem::exists(path_output_name)) @@ -288,10 +288,29 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: std::string new_output_name; std::getline(f, new_output_name); f.close(); + + if (host == "File") { + boost::filesystem::path op(new_output_name); + if (op.is_relative() && op.has_filename() && op.parent_path().empty()) { + // Is this just a filename? Make it an absolute path. + auto outpath = boost::filesystem::path(output_name).parent_path(); + outpath /= op.string(); + new_output_name = outpath.string(); + } + else { + if (! op.is_absolute() || ! op.has_filename()) + throw Slic3r::RuntimeError("Unable to parse desired new path from output name file"); + } + if (! boost::filesystem::exists(op.parent_path())) + throw Slic3r::RuntimeError(Slic3r::format("Output directory does not exist: %1%", op.parent_path().string())); + } + BOOST_LOG_TRIVIAL(trace) << "Post-processing script changed the file name from " << output_name << " to " << new_output_name; output_name = new_output_name; } catch (const std::exception &err) { - BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed reading a file %1% carrying the final name / path of a G-code file: %2%", path_output_name, err.what()); + throw Slic3r::RuntimeError(Slic3r::format("run_post_process_scripts: Failed reading a file %1% " + "carrying the final name / path of a G-code file: %2%", + path_output_name, err.what())); } remove_output_name_file(); } From 7e0a2644e2e29d4d7be3d5961731fd26f991a35d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 13:38:28 +0200 Subject: [PATCH 070/240] Fixup of previous commit --- src/libslic3r/GCode/PostProcessor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 453fddaa3e..27b2254e9e 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -290,10 +290,11 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: f.close(); if (host == "File") { - boost::filesystem::path op(new_output_name); + namespace fs = boost::filesystem; + fs::path op(new_output_name); if (op.is_relative() && op.has_filename() && op.parent_path().empty()) { // Is this just a filename? Make it an absolute path. - auto outpath = boost::filesystem::path(output_name).parent_path(); + auto outpath = fs::path(output_name).parent_path(); outpath /= op.string(); new_output_name = outpath.string(); } @@ -301,8 +302,9 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: if (! op.is_absolute() || ! op.has_filename()) throw Slic3r::RuntimeError("Unable to parse desired new path from output name file"); } - if (! boost::filesystem::exists(op.parent_path())) - throw Slic3r::RuntimeError(Slic3r::format("Output directory does not exist: %1%", op.parent_path().string())); + if (! fs::exists(fs::path(new_output_name).parent_path())) + throw Slic3r::RuntimeError(Slic3r::format("Output directory does not exist: %1%", + fs::path(new_output_name).parent_path().string())); } BOOST_LOG_TRIVIAL(trace) << "Post-processing script changed the file name from " << output_name << " to " << new_output_name; From 9632e6fa6a675929db5e64bf24569815983eb587 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 12:11:19 +0200 Subject: [PATCH 071/240] New icons for mmu painting, fuzzy skin and object info items --- resources/icons/fuzzy_skin.svg | 15 ++-- resources/icons/info.png | Bin 308 -> 0 bytes resources/icons/info.svg | 86 ++++------------------ resources/icons/mmu_segmentation.svg | 28 +++++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 5 files changed, 55 insertions(+), 76 deletions(-) delete mode 100644 resources/icons/info.png create mode 100644 resources/icons/mmu_segmentation.svg diff --git a/resources/icons/fuzzy_skin.svg b/resources/icons/fuzzy_skin.svg index b8ba0a6513..f1ddc6b332 100644 --- a/resources/icons/fuzzy_skin.svg +++ b/resources/icons/fuzzy_skin.svg @@ -1,8 +1,13 @@ - + - - - + width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> + + diff --git a/resources/icons/info.png b/resources/icons/info.png deleted file mode 100644 index 9eeee9b3cdbac7fd819d3f4c3ee85414c5f1ed50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmV-40n7f0P)6|`_Rtm|7y^1~Ew=AW5=n0W(fT;mbLc<=CvuP!m`yl+rpgAzqp8NMnw zN!+2v9C;$1N30I+zs_}ZYEa-QtZ4bm;QmokMfEnO_zs(PV)Mv3c0VL!_bF(`IW#IJ zSafGP0SinrjIiz@J%^>R#;Ci-vyowo@ddeKY%{FzAieuO -image/svg+xml - - - - - - - - - + + + + + diff --git a/resources/icons/mmu_segmentation.svg b/resources/icons/mmu_segmentation.svg new file mode 100644 index 0000000000..715e6ec281 --- /dev/null +++ b/resources/icons/mmu_segmentation.svg @@ -0,0 +1,28 @@ + + + + + + diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index eda95a2a55..7461b09220 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -103,7 +103,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); - m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9)); + m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); From 36050166965b374fd06249ce8396c29924a6c09a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 15:06:29 +0200 Subject: [PATCH 072/240] Speed-up of clipping plane --- src/slic3r/GUI/MeshUtils.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index d889bc538b..588e50dab2 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -87,16 +87,16 @@ void MeshClipper::recalculate_triangles() float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm()); // Now do the cutting - MeshSlicingParamsEx slicing_params; + MeshSlicingParams slicing_params; slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up, Vec3d::UnitZ())); assert(m_mesh->has_shared_vertices()); - std::vector list_of_expolys = slice_mesh_ex(m_mesh->its, std::vector{height_mesh}, slicing_params); + ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); if (m_negative_mesh && !m_negative_mesh->empty()) { assert(m_negative_mesh->has_shared_vertices()); - std::vector neg_polys = slice_mesh_ex(m_negative_mesh->its, std::vector{height_mesh}, slicing_params); - list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front()); + ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params)); + expolys = diff_ex(expolys, neg_expolys); } // Triangulate and rotate the cut into world coords: @@ -131,7 +131,7 @@ void MeshClipper::recalculate_triangles() if (std::abs(normal_old.dot(m_plane.get_normal().normalized())) > 0.99) { // The cuts are parallel, show all or nothing. if (offset < height_mesh) - list_of_expolys.front().clear(); + expolys.clear(); } else { // The cut is a horizontal plane defined by z=height_mesh. // ax+by+e=0 is the line of intersection with the limiting plane. @@ -155,14 +155,14 @@ void MeshClipper::recalculate_triangles() // from the cut. The coordinates must not overflow after the transform, // make the rectangle a bit smaller. coord_t size = (std::numeric_limits::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; - ExPolygons ep {ExPolygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; + Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; ep.front().rotate(angle); ep.front().translate(scale_(-e * a), scale_(-e * b)); - list_of_expolys.front() = diff_ex(list_of_expolys.front(), ep.front()); + expolys = diff_ex(expolys, ep); } } - m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); + m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); m_vertex_array.release_geometry(); for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { From 3a2a9d8500e5f7c1b7d3641e36cf960caaeba2b2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 11 Aug 2021 15:40:33 +0200 Subject: [PATCH 073/240] Follow up c0a00f4e7016685f4afd34aca3605125dff38c66 - Check unsaved changes only if project wasn't saved --- src/slic3r/GUI/MainFrame.cpp | 18 +++++++++++------- src/slic3r/GUI/Plater.cpp | 11 +++++------ src/slic3r/GUI/Plater.hpp | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6a900714ea..18a71f681d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -220,15 +220,19 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S return; } - if (m_plater != nullptr && !m_plater->save_project_if_dirty()) { - event.Veto(); - return; + if (m_plater != nullptr) { + int saved_project = m_plater->save_project_if_dirty(); + if (saved_project == wxID_CANCEL) { + event.Veto(); + return; + } + // check unsaved changes only if project wasn't saved + else if (saved_project == wxID_NO && event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) { + event.Veto(); + return; + } } - if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) { - event.Veto(); - return; - } if (event.CanVeto() && !wxGetApp().check_print_host_queue()) { event.Veto(); return; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fc77e9b9d8..b5a2292761 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1576,20 +1576,19 @@ struct Plater::priv bool is_project_dirty() const { return dirty_state.is_dirty(); } void update_project_dirty_from_presets() { dirty_state.update_from_presets(); } - bool save_project_if_dirty() { + int save_project_if_dirty() { + int res = wxID_NO; if (dirty_state.is_dirty()) { MainFrame* mainframe = wxGetApp().mainframe; if (mainframe->can_save_as()) { //wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL); MessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL); - int res = dlg.ShowModal(); + res = dlg.ShowModal(); if (res == wxID_YES) mainframe->save_project_as(wxGetApp().plater()->get_project_filename()); - else if (res == wxID_CANCEL) - return false; } } - return true; + return res; } void reset_project_dirty_after_save() { dirty_state.reset_after_save(); } void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); } @@ -4647,7 +4646,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) bool Plater::is_project_dirty() const { return p->is_project_dirty(); } void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); } -bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); } +int Plater::save_project_if_dirty() { return p->save_project_if_dirty(); } void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); } void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2b1a83ba28..3556756e23 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -140,7 +140,7 @@ public: bool is_project_dirty() const; void update_project_dirty_from_presets(); - bool save_project_if_dirty(); + int save_project_if_dirty(); void reset_project_dirty_after_save(); void reset_project_dirty_initial_presets(); #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW From 83ab034f9a8769e2d2e8ce306bafa16682150234 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 11 Aug 2021 14:34:49 +0200 Subject: [PATCH 074/240] Fixed ENTER for ComboBoxes and TextCtrls in Settings Tabs (related to #6692) + Code cleaning : Deleted unused set_focus --- src/slic3r/GUI/Field.cpp | 58 ++++++++------------------------- src/slic3r/GUI/Field.hpp | 19 +++++------ src/slic3r/GUI/OptionsGroup.cpp | 11 ------- src/slic3r/GUI/OptionsGroup.hpp | 2 -- 4 files changed, 22 insertions(+), 68 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d7d4745319..69072d3750 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -73,8 +73,6 @@ Field::~Field() { if (m_on_kill_focus) m_on_kill_focus = nullptr; - if (m_on_set_focus) - m_on_set_focus = nullptr; if (m_on_change) m_on_change = nullptr; if (m_back_to_initial_value) @@ -156,15 +154,6 @@ void Field::on_kill_focus() m_on_kill_focus(m_opt_id); } -void Field::on_set_focus(wxEvent& event) -{ - // to allow the default behavior - event.Skip(); - // call the registered function if it is available - if (m_on_set_focus!=nullptr) - m_on_set_focus(m_opt_id); -} - void Field::on_change_field() { // std::cerr << "calling Field::_on_change \n"; @@ -507,13 +496,11 @@ void TextCtrl::BUILD() { e.Skip(); temp->GetToolTip()->Enable(true); #endif // __WXGTK__ - bEnterPressed = true; + EnterPressed enter(this); propagate_value(); }), temp->GetId()); } - temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId()); - temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) { //! to allow the default handling @@ -530,26 +517,11 @@ void TextCtrl::BUILD() { temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) { e.Skip(); -#ifdef __WXOSX__ - // OSX issue: For some unknown reason wxEVT_KILL_FOCUS is emitted twice in a row in some cases - // (like when information dialog is shown during an update of the option value) - // Thus, suppress its second call - if (bKilledFocus) - return; - bKilledFocus = true; -#endif // __WXOSX__ - #if !defined(__WXGTK__) temp->GetToolTip()->Enable(true); #endif // __WXGTK__ - if (bEnterPressed) - bEnterPressed = false; - else + if (!bEnterPressed) propagate_value(); -#ifdef __WXOSX__ - // After processing of KILL_FOCUS event we should to invalidate a bKilledFocus flag - bKilledFocus = false; -#endif // __WXOSX__ }), temp->GetId()); /* // select all text using Ctrl+A @@ -990,26 +962,13 @@ void Choice::BUILD() { if (m_is_editable) { temp->Bind(wxEVT_KILL_FOCUS, [this](wxEvent& e) { e.Skip(); - if (bKilledFocus) - return; - - bKilledFocus = true; - - if (bEnterPressed) - bEnterPressed = false; - else + if (!bEnterPressed) propagate_value(); - // After processing of KILL_FOCUS event we should to invalidate a bKilledFocus flag - bKilledFocus = false; } ); temp->Bind(wxEVT_TEXT_ENTER, [this, temp](wxEvent& e) { -#ifdef _WIN32 - temp->SetFocus(); -#else - bEnterPressed = true; + EnterPressed enter(this); propagate_value(); -#endif //_WIN32 } ); } @@ -1161,6 +1120,15 @@ void Choice::set_value(const boost::any& value, bool change_event) } else field->SetSelection(idx); + + if (!m_value.empty() && m_opt.opt_key == "fill_density") { + // If m_value was changed before, then update m_value here too to avoid case + // when control's value is already changed from the ConfigManipulation::update_print_fff_config(), + // but m_value doesn't respect it. + if (double val; text_value.ToDouble(&val)) + m_value = val; + } + break; } case coEnum: { diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 34a2481b52..8bdf4b8d80 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -52,10 +52,17 @@ protected: //! in another case we can't unfocused control at all void on_kill_focus(); /// Call the attached on_change method. - void on_set_focus(wxEvent& event); - /// Call the attached on_change method. void on_change_field(); + class EnterPressed { + public: + EnterPressed(Field* field) : + m_parent(field){ m_parent->set_enter_pressed(true); } + ~EnterPressed() { m_parent->set_enter_pressed(false); } + private: + Field* m_parent; + }; + public: /// Call the attached m_back_to_initial_value method. void on_back_to_initial_value(); @@ -69,9 +76,6 @@ public: /// Function object to store callback passed in from owning object. t_kill_focus m_on_kill_focus {nullptr}; - /// Function object to store callback passed in from owning object. - t_kill_focus m_on_set_focus {nullptr}; - /// Function object to store callback passed in from owning object. t_change m_on_change {nullptr}; @@ -236,10 +240,6 @@ class TextCtrl : public Field { void change_field_value(wxEvent& event); #endif //__WXGTK__ -#ifdef __WXOSX__ - bool bKilledFocus = false; -#endif // __WXOSX__ - public: TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} @@ -342,7 +342,6 @@ public: class Choice : public Field { using Field::Field; - bool bKilledFocus = false; public: Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 728981f0a0..b57b7f3026 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -88,11 +88,6 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co if (!m_disabled) this->on_kill_focus(opt_id); }; - field->m_on_set_focus = [this](const std::string& opt_id) { - //! This function will be called from Field. - if (!m_disabled) - this->on_set_focus(opt_id); - }; field->m_parent = parent(); field->m_back_to_initial_value = [this](std::string opt_id) { @@ -514,12 +509,6 @@ void OptionsGroup::clear_fields_except_of(const std::vector left_fi } } -void OptionsGroup::on_set_focus(const std::string& opt_key) -{ - if (m_set_focus != nullptr) - m_set_focus(opt_key); -} - void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) { if (m_on_change != nullptr) m_on_change(opt_id, value); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 10cea5ddad..592f2cd761 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -100,7 +100,6 @@ public: // To be called when the field loses focus, to assign a new initial value to the field. // Used by the relative position / rotation / scale manipulation fields of the Object Manipulation UI. t_kill_focus m_fill_empty_value { nullptr }; - t_kill_focus m_set_focus { nullptr }; std::function m_get_initial_config{ nullptr }; std::function m_get_sys_config{ nullptr }; std::function have_sys_config{ nullptr }; @@ -208,7 +207,6 @@ protected: const t_field& build_field(const Option& opt); virtual void on_kill_focus(const std::string& opt_key) {}; - virtual void on_set_focus(const std::string& opt_key); virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value); virtual void back_to_initial_value(const std::string& opt_key) {} virtual void back_to_sys_value(const std::string& opt_key) {} From b14faf627bd007b494663a48972e995cfe19c1cf Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 15:52:52 +0200 Subject: [PATCH 075/240] Do not show sinking contours when a gizmo hides some objects --- src/slic3r/GUI/3DScene.cpp | 34 ++++++++++++----------- src/slic3r/GUI/3DScene.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 ++++ src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 2 +- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 2babc516ab..992ed7353f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -843,12 +843,13 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab volume.first->set_render_color(); // render sinking contours of non-hovered volumes - if (volume.first->is_sinking() && !volume.first->is_below_printbed() && - volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) { - shader->stop_using(); - volume.first->render_sinking_contours(); - shader->start_using(); - } + if (m_show_sinking_contours) + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && + volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) { + shader->stop_using(); + volume.first->render_sinking_contours(); + shader->start_using(); + } glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); @@ -887,17 +888,18 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); } - for (GLVolumeWithIdAndZ& volume : to_render) { - // render sinking contours of hovered/displaced volumes - if (volume.first->is_sinking() && !volume.first->is_below_printbed() && - (volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) { - shader->stop_using(); - glsafe(::glDepthFunc(GL_ALWAYS)); - volume.first->render_sinking_contours(); - glsafe(::glDepthFunc(GL_LESS)); - shader->start_using(); + if (m_show_sinking_contours) + for (GLVolumeWithIdAndZ& volume : to_render) { + // render sinking contours of hovered/displaced volumes + if (volume.first->is_sinking() && !volume.first->is_below_printbed() && + (volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) { + shader->stop_using(); + glsafe(::glDepthFunc(GL_ALWAYS)); + volume.first->render_sinking_contours(); + glsafe(::glDepthFunc(GL_LESS)); + shader->start_using(); + } } - } #else glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index a257db56ac..e5298ca931 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -539,6 +539,7 @@ private: }; Slope m_slope; + bool m_show_sinking_contours = false; public: GLVolumePtrs volumes; @@ -608,6 +609,7 @@ public: float get_slope_normal_z() const { return m_slope.normal_z; } void set_slope_normal_z(float normal_z) { m_slope.normal_z = normal_z; } void set_default_slope_normal_z() { m_slope.normal_z = -::cos(Geometry::deg2rad(90.0f - 45.0f)); } + void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; } // returns true if all the volumes are completely contained in the print volume // returns the containment state in the given out_state, if non-null diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f366100acc..93271d796e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5107,6 +5107,7 @@ void GLCanvas3D::_render_objects() m_volumes.set_z_range(-FLT_MAX, FLT_MAX); m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); if (shader != nullptr) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 7461b09220..fc789c3d2a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1248,6 +1248,12 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const } +bool GLGizmosManager::is_hiding_instances() const +{ + return (m_common_gizmos_data->instances_hider() && m_common_gizmos_data->instances_hider()->is_valid()); +} + + int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const { return m_gizmos[type]->get_shortcut_key(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index cdfb3f6ff7..188c9e9141 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -122,7 +122,6 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; bool m_serializing; - //std::unique_ptr m_common_gizmos_data; std::unique_ptr m_common_gizmos_data; public: @@ -218,6 +217,7 @@ public: bool wants_reslice_supports_on_undo() const; bool is_in_editing_mode(bool error_notification = false) const; + bool is_hiding_instances() const; void render_current_gizmo() const; void render_current_gizmo_for_picking_pass() const; From fc1054c61d74a25327552bb1d7c400052589101d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 16:16:57 +0200 Subject: [PATCH 076/240] Restored info icon replaced by mistake --- resources/icons/info.svg | 86 +++++++++++++++++++++----- resources/icons/objlist_info.svg | 17 +++++ src/slic3r/GUI/MsgDialog.cpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 2 +- 4 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 resources/icons/objlist_info.svg diff --git a/resources/icons/info.svg b/resources/icons/info.svg index 6e11a9b778..276b260610 100644 --- a/resources/icons/info.svg +++ b/resources/icons/info.svg @@ -1,17 +1,71 @@ - - - - - + +image/svg+xml + + + + + + + + + diff --git a/resources/icons/objlist_info.svg b/resources/icons/objlist_info.svg new file mode 100644 index 0000000000..6e11a9b778 --- /dev/null +++ b/resources/icons/objlist_info.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 92b31cc520..28bb9cc93f 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -202,7 +202,7 @@ MessageDialog::MessageDialog(wxWindow* parent, if (style & wxCANCEL) add_btn(wxID_CANCEL); logo->SetBitmap(create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" : - style & wxICON_INFORMATION ? "info.png" : + style & wxICON_INFORMATION ? "info" : style & wxICON_QUESTION ? "question" : "PrusaSlicer_192px_grayscale.png", this, 84)); wxGetApp().UpdateDlgDarkUI(this); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 3a114fd2c7..df2e1a13b9 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -37,7 +37,7 @@ void ObjectDataViewModelNode::init_container() static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; -static constexpr char InfoIcon[] = "info"; +static constexpr char InfoIcon[] = "objlist_info"; ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, From 4df2c969a580ab356bc4c75f1e0efcabad169282 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 11 Aug 2021 17:14:47 +0200 Subject: [PATCH 077/240] Hint notification changes in hints.ini Hint notification: Just one button in Help menu. Hint notification: New naming - Tip of the day Notifications: no scrolling. Hint notification: fadeout colors. --- resources/data/hints.ini | 4 +++ src/slic3r/GUI/HintNotification.cpp | 30 ++++--------------- src/slic3r/GUI/HintNotification.hpp | 4 +-- src/slic3r/GUI/MainFrame.cpp | 4 +-- src/slic3r/GUI/NotificationManager.cpp | 40 +++++++------------------- src/slic3r/GUI/Preferences.cpp | 2 +- 6 files changed, 23 insertions(+), 61 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 6127fecc6d..bfe2782d7b 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -103,6 +103,7 @@ documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 +disabled_tags = SLA [hint:Solid infill threshold area] text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set theSolid infill threshold area.(Expert mode only.) @@ -176,11 +177,13 @@ disabled_tags = SLA; simple text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer +disabled_tags = SLA [hint:Insert Custom G-code] text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer +disabled_tags = SLA [hint:Configuration snapshots] text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - Configuration snapshots menu. Read more in the documentation. @@ -204,6 +207,7 @@ hypertext_preferences_page = 2 text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 +disabled_tags = SLA [hint:Fullscreen mode] text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 2f0f924493..ef35bba64d 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -662,11 +662,7 @@ void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& im close(); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); //render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); @@ -722,11 +718,7 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp wxGetApp().open_preferences(2); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); // preferences button is in place of minimize button m_minimize_b_visible = true; } @@ -758,11 +750,7 @@ void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapp retrieve_data(); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { @@ -788,11 +776,7 @@ void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, con { } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { @@ -846,11 +830,7 @@ void NotificationManager::HintNotification::render_documentation_button(ImGuiWra open_documentation(); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } void NotificationManager::HintNotification::open_documentation() diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index ff81c66a2a..78e02a848c 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -99,8 +99,8 @@ protected: float m_close_b_y { 0 }; float m_close_b_w { 0 }; // hover of buttons - size_t m_docu_hover_time { 0 }; - size_t m_prefe_hover_time{ 0 }; + long m_docu_hover_time { 0 }; + long m_prefe_hover_time{ 0 }; }; } //namespace Slic3r diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 18a71f681d..ef1f236a32 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1078,9 +1078,7 @@ static wxMenu* generate_help_menu() else append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), GCODEVIEWER_APP_NAME), _L("Show about dialog"), [](wxCommandEvent&) { Slic3r::GUI::about(); }); - append_menu_item(helpMenu, wxID_ANY, _L("Next Hint notification"), _L("Opens another Hint notification."), - [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(true); }); - append_menu_item(helpMenu, wxID_ANY, _L("Reopen Hint notification"), _L("Opens Hint notification in bottom right corner."), + append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the day"), _L("Opens Tip of the day notification in bottom right corner or shows another tip if already opened."), [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(false); }); helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"), diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 84f31a03f8..ec54487c6a 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -218,6 +218,7 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init if (m_state == EState::FadingOut) { push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity); push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity); + push_style_color(ImGuiCol_ButtonHovered, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered), true, m_current_fade_opacity); fading_pop = true; } @@ -229,7 +230,7 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init m_id = m_id_provider.allocate_id(); std::string name = "!!Ntfctn" + std::to_string(m_id); - if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) { + if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { ImVec2 win_size = ImGui::GetWindowSize(); render_left_sign(imgui); @@ -245,7 +246,7 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init ImGui::PopStyleColor(); if (fading_pop) - ImGui::PopStyleColor(2); + ImGui::PopStyleColor(3); } bool NotificationManager::PopNotification::push_background_color() { @@ -440,9 +441,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, close(); } } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(3); //hover color ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); @@ -501,11 +500,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img { close(); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui) @@ -545,11 +540,7 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& m_multiline = false; } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); m_minimize_b_visible = true; } bool NotificationManager::PopNotification::on_text_click() @@ -790,11 +781,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); close(); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } bool NotificationManager::ExportFinishedNotification::on_text_click() { @@ -1054,11 +1041,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu { wxGetApp().printhost_job_queue().cancel(m_job_id - 1); } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(5); } //------UpdatedItemsInfoNotification------- void NotificationManager::UpdatedItemsInfoNotification::count_spaces() @@ -1096,7 +1079,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty case InfoItemType::MmuSegmentation: text += _utf8("multimaterial painting.\n"); break; case InfoItemType::VariableLayerHeight: text += _utf8("variable layer height.\n"); break; case InfoItemType::Sinking: text += _utf8("Partial sinking.\n"); break; - default: text.clear(); break; + default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } NotificationData data { get_data().type, get_data().level , get_data().duration, text }; @@ -1380,10 +1363,7 @@ void NotificationManager::push_hint_notification(bool open_next) { for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::DidYouKnowHint) { - if (open_next) - (dynamic_cast(notification.get()))->open_next(); - else - notification->set_hovered(); + (dynamic_cast(notification.get()))->open_next(); return; } } diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 7ae10c5772..50a5993a9b 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -342,7 +342,7 @@ void PreferencesDialog::build(size_t selected_tab) m_optgroup_gui->append_single_option_line(option); #endif - def.label = L("Show \"Did you know\" hints after start"); + def.label = L("Show \"Tip of the day\" notification after start"); def.type = coBool; def.tooltip = L("If enabled, useful hints are displayed at startup."); def.set_default_value(new ConfigOptionBool{ app_config->get("show_hints") == "1" }); From 7f2ad140b63711e0bf4db6cbf19fc0a60983f296 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 11 Aug 2021 22:03:06 +0200 Subject: [PATCH 078/240] Fix a crash recently introduced by b14faf6 (reported in #6794) --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index fc789c3d2a..6cbb9ae401 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1250,7 +1250,9 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const bool GLGizmosManager::is_hiding_instances() const { - return (m_common_gizmos_data->instances_hider() && m_common_gizmos_data->instances_hider()->is_valid()); + return (m_common_gizmos_data + && m_common_gizmos_data->instances_hider() + && m_common_gizmos_data->instances_hider()->is_valid()); } From 23b1bc07a4922db2fdaa20948334f88b6ecb4af3 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 09:29:47 +0200 Subject: [PATCH 079/240] narrow mr. clippy logo --- resources/icons/notification_clippy.svg | 283 ++++++++++++++++++++---- src/slic3r/GUI/HintNotification.cpp | 2 +- 2 files changed, 236 insertions(+), 49 deletions(-) diff --git a/resources/icons/notification_clippy.svg b/resources/icons/notification_clippy.svg index e7eb207fe7..eaa922e5aa 100644 --- a/resources/icons/notification_clippy.svg +++ b/resources/icons/notification_clippy.svg @@ -1,49 +1,236 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index ef35bba64d..85d4629e6c 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -354,7 +354,7 @@ void NotificationManager::HintNotification::count_spaces() std::string text; text = ImGui::WarningMarker; float picture_width = ImGui::CalcTextSize(text.c_str()).x; - m_left_indentation = picture_width * 2 + m_line_height / 2; + m_left_indentation = picture_width * 1.5f + m_line_height / 2; // no left button picture //m_left_indentation = m_line_height; From 4e9a94949787634910ab3dae167ad2e1cb0882d1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 08:46:24 +0200 Subject: [PATCH 080/240] narrow mr. clippy with outline --- resources/icons/notification_clippy.svg | 496 +++++++++++++----------- 1 file changed, 270 insertions(+), 226 deletions(-) diff --git a/resources/icons/notification_clippy.svg b/resources/icons/notification_clippy.svg index eaa922e5aa..406ad2bfa6 100644 --- a/resources/icons/notification_clippy.svg +++ b/resources/icons/notification_clippy.svg @@ -7,230 +7,274 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - sodipodi:docname="notification_clippy.svg" - id="svg98" version="1.1" - viewBox="0 0 99.999999 100" - height="100" - width="100"> - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="Layer_1" + x="0px" + y="0px" + viewBox="0 0 105.1 105.1" + xml:space="preserve" + sodipodi:docname="notification_clippy.svg" + width="105.1" + height="105.1" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)">image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 52718e96f5de49162c17f3b0335e982a2580c73e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 10 Aug 2021 15:35:38 +0200 Subject: [PATCH 081/240] Moved flatpak files from resources/data to src/platform/unix (#6774) --- CMakeLists.txt | 2 +- .../platform/unix}/flatpak/com.prusa3d.PrusaSlicer.desktop | 0 .../platform/unix}/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {resources/data => src/platform/unix}/flatpak/com.prusa3d.PrusaSlicer.desktop (100%) rename {resources/data => src/platform/unix}/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 268380dc28..9a69d3bbfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -569,7 +569,7 @@ elseif (SLIC3R_FHS) # CMAKE_INSTALL_FULL_DATAROOTDIR: read-only architecture-independent data root (share) set(SLIC3R_FHS_RESOURCES "${CMAKE_INSTALL_FULL_DATAROOTDIR}/PrusaSlicer") install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/ DESTINATION ${SLIC3R_FHS_RESOURCES} - PATTERN "*/data" EXCLUDE PATTERN "*/udev" EXCLUDE + PATTERN "*/udev" EXCLUDE ) install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) diff --git a/resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop similarity index 100% rename from resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop rename to src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop diff --git a/resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml similarity index 100% rename from resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml rename to src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml From 021688fe500536debbe5c058d9961b8e084874f2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 12 Aug 2021 10:17:59 +0200 Subject: [PATCH 082/240] Fix place on bed and sinking instances: all sinking objects were incorrectly shifted to bed when 'place on face' was applied to any object. --- src/slic3r/GUI/GLCanvas3D.cpp | 17 +++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 93271d796e..529056e999 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3455,12 +3455,17 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) // stores current min_z of instances std::map, double> min_zs; - if (!snapshot_type.empty()) { - for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { - const ModelObject* obj = m_model->objects[i]; - for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + if (snapshot_type.empty() && m_selection.get_object_idx() == i) { + // This means we are flattening this object. In that case pretend + // that it is not sinking (even if it is), so it is placed on bed + // later on (whatever is sinking will be left sinking). + min_zs[{ i, j }] = SINKING_Z_THRESHOLD; + } else min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); - } + } } @@ -3502,7 +3507,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) ModelObject* m = m_model->objects[i.first]; const double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking - if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) { + if (min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) { const Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 094805abc4..3e5c12eec4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -42,6 +42,8 @@ std::string GLGizmoFlatten::on_get_name() const bool GLGizmoFlatten::on_is_activable() const { + // This is assumed in GLCanvas3D::do_rotate, do not change this + // without updating that function too. return m_parent.get_selection().is_single_full_instance(); } From 131d2ace50ae313fb67d1f5d5ede988c7d776cf8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 10:15:46 +0200 Subject: [PATCH 083/240] =?UTF-8?q?Check=20min/max=20values=20for=20SpinCt?= =?UTF-8?q?rls=C2=A0in=20Settings=20Tabs=20+=20Set=20max=20value=20for=20"?= =?UTF-8?q?extruders=5Fcount"=20to=20256?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/slic3r/GUI/Field.cpp | 30 +++++++++++++++++++----------- src/slic3r/GUI/Tab.cpp | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 69072d3750..1aaac54690 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -823,7 +823,7 @@ void SpinCtrl::BUILD() { bEnterPressed = true; }), temp->GetId()); - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) + temp->Bind(wxEVT_TEXT, ([this, temp](wxCommandEvent e) { // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value // # when it was changed from the text control, so the on_change callback @@ -833,20 +833,28 @@ void SpinCtrl::BUILD() { long value; const bool parsed = e.GetString().ToLong(&value); - tmp_value = parsed && value >= INT_MIN && value <= INT_MAX ? (int)value : UNDEF_VALUE; - + if (!parsed || value < INT_MIN || value > INT_MAX) + tmp_value = UNDEF_VALUE; + else { + tmp_value = std::min(std::max((int)value, m_opt.min), m_opt.max); #ifdef __WXOSX__ - // Forcibly set the input value for SpinControl, since the value - // inserted from the keyboard or clipboard is not updated under OSX - if (tmp_value != UNDEF_VALUE) { - wxSpinCtrl* spin = static_cast(window); - spin->SetValue(tmp_value); - + // Forcibly set the input value for SpinControl, since the value + // inserted from the keyboard or clipboard is not updated under OSX + temp->SetValue(tmp_value); // But in SetValue() is executed m_text_ctrl->SelectAll(), so // discard this selection and set insertion point to the end of string - spin->GetText()->SetInsertionPointEnd(); - } + temp->GetText()->SetInsertionPointEnd(); +#else + // update value for the control only if it was changed in respect to the Min/max values + if (tmp_value != (int)value) { + temp->SetValue(tmp_value); + // But after SetValue() cursor ison the first position + // so put it to the end of string + int pos = std::to_string(tmp_value).length(); + temp->SetSelection(pos, pos); + } #endif + } }), temp->GetId()); temp->SetToolTip(get_tooltip_text(text_value)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0d1ce1092c..5c8fed787f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2208,6 +2208,7 @@ void TabPrinter::build_fff() def.label = L("Extruders"); def.tooltip = L("Number of extruders of the printer."); def.min = 1; + def.max = 256; def.mode = comExpert; Option option(def, "extruders_count"); optgroup->append_single_option_line(option); From f3371a3b84db8dffe6a9eb05c361ed2afeb9ffd9 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 08:49:38 +0200 Subject: [PATCH 084/240] warning fix --- src/slic3r/GUI/HintNotification.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 85d4629e6c..0e4d1a5a33 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -850,7 +850,7 @@ void NotificationManager::HintNotification::retrieve_data(int recursion_counter) { // Content for different user - retrieve another size_t count = HintDatabase::get_instance().get_count(); - if (count < recursion_counter) { + if ((int)count < recursion_counter) { BOOST_LOG_TRIVIAL(error) << "Hint notification failed to load data due to recursion counter."; } else { retrieve_data(recursion_counter + 1); From 483684f702cbc4fed815ac27088c368ca2f51695 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 12:24:58 +0200 Subject: [PATCH 085/240] Delete inline in some functions --- src/slic3r/GUI/HintNotification.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 0e4d1a5a33..dd215052db 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -37,7 +37,7 @@ enum TagCheckResult TagCheckNotCompatible }; // returns if in mode defined by tag -inline TagCheckResult tag_check_mode(const std::string& tag) +TagCheckResult tag_check_mode(const std::string& tag) { std::vector allowed_tags = {"simple", "advanced", "expert"}; if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) @@ -50,7 +50,7 @@ inline TagCheckResult tag_check_mode(const std::string& tag) return TagCheckNotCompatible; } -inline TagCheckResult tag_check_tech(const std::string& tag) +TagCheckResult tag_check_tech(const std::string& tag) { std::vector allowed_tags = { "FFF", "MMU", "SLA" }; if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { @@ -68,7 +68,7 @@ inline TagCheckResult tag_check_tech(const std::string& tag) return TagCheckNotCompatible; } -inline TagCheckResult tag_check_system(const std::string& tag) +TagCheckResult tag_check_system(const std::string& tag) { std::vector allowed_tags = { "Windows", "Linux", "OSX" }; if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { @@ -97,7 +97,7 @@ inline TagCheckResult tag_check_system(const std::string& tag) } // return true if NOT in disabled mode. -inline bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) +bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) { if (disabled_tags.empty() && enabled_tags.empty()) return true; @@ -173,7 +173,7 @@ inline bool tags_check(const std::string& disabled_tags, const std::string& enab } return true; } -inline void launch_browser_if_allowed(const std::string& url) +void launch_browser_if_allowed(const std::string& url) { if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") wxLaunchDefaultBrowser(url); From 37ba18a8c32b43c73dbb8fda6c0ee2abfc5c7ba8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 12:39:52 +0200 Subject: [PATCH 086/240] DoubleSlider: Init extruder sequence in respect to the extruders count --- src/slic3r/GUI/DoubleSlider.cpp | 4 ++++ src/slic3r/GUI/DoubleSlider.hpp | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 94d6a2c647..36a4f6e5cf 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -384,6 +384,10 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one post_ticks_changed_event(); + // init extruder sequence in respect to the extruders count + if (m_ticks.empty()) + m_extruders_sequence.init(m_extruder_colors.size()); + if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 601cbc7088..0f663f6630 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -180,6 +180,13 @@ struct ExtrudersSequence return;// last item can't be deleted extruders.erase(extruders.begin() + pos); } + + void init(size_t extruders_count) + { + extruders.clear(); + for (size_t extruder = 0; extruder < extruders_count; extruder++) + extruders.push_back(extruder); + } }; class Control : public wxControl From 66435887eadea3f03b2044095334510a433480be Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 12:51:27 +0200 Subject: [PATCH 087/240] Revert of the 84c6199015fde4a7e04fd4cd214580f9f1ac5091 Note: When ProgressDialog has no-nullptr parent, then PrusaSlicer doesn't lose the focus at the end of model fixing, but start to flickering during fixing of the big models. --- src/slic3r/GUI/Plater.cpp | 2 ++ src/slic3r/Utils/FixModelByWin10.cpp | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b5a2292761..b85cc3cd79 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3503,6 +3503,8 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = ModelObject* mo = model.objects[obj_idx]; fix_model_by_win10_sdk_gui(*mo, vol_idx); q->changed_mesh(obj_idx); + // workaround to fix the issue, when PrusaSlicer lose a focus after model fixing + q->SetFocus(); } void Plater::priv::set_current_panel(wxPanel* panel) diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index a3689d9276..2fbd0d9374 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -36,8 +36,6 @@ #include "../GUI/GUI.hpp" #include "../GUI/I18N.hpp" #include "../GUI/MsgDialog.hpp" -#include "../GUI/GUI_App.hpp" -#include "../GUI/MainFrame.hpp" #include #include @@ -343,7 +341,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) wxProgressDialog progress_dialog( _L("Model fixing"), _L("Exporting model") + "...", - 100, GUI::wxGetApp().mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). bool success = false; From c44715f9e0646caecdb4cf54c52df74853f78573 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 12 Aug 2021 12:46:18 +0200 Subject: [PATCH 088/240] Moved 'brim_offset' option into Advanced mode (same as EFC) --- src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ed7961ce10..267ea602fe 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -493,7 +493,7 @@ void PrintConfigDef::init_fff_params() def->category = L("Skirt and brim"); def->tooltip = L("The offset of the brim from the printed object."); def->sidetext = L("mm"); - def->mode = comSimple; + def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0)); def = this->add("clip_multipart_objects", coBool); From 2be10569e20ba440bf9c2d9c8f830843e9e93565 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 12 Aug 2021 13:38:14 +0200 Subject: [PATCH 089/240] Add painting gizmos shortcuts into keyboard shortcuts dialog --- src/slic3r/GUI/KBShortcutsDialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 77ce3d83e8..0bed7eb74f 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -151,6 +151,9 @@ void KBShortcutsDialog::fill_shortcuts() { "F", L("Gizmo Place face on bed") }, { "H", L("Gizmo SLA hollow") }, { "L", L("Gizmo SLA support points") }, + { "L", L("Gizmo FDM paint-on supports") }, + { "P", L("Gizmo FDM paint-on seam") }, + { "N", L("Gizmo Multi Material painting") }, { "Esc", L("Unselect gizmo or clear selection") }, { "K", L("Change camera type (perspective, orthographic)") }, { "B", L("Zoom to Bed") }, From 3ca2b2bc71e73b965ad222971222194c4d341250 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 15:02:24 +0200 Subject: [PATCH 090/240] ObjectList: Added separate icons for InfoItems --- src/slic3r/GUI/ObjectDataViewModel.cpp | 29 ++++++++++++++++++-------- src/slic3r/GUI/ObjectDataViewModel.hpp | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index df2e1a13b9..96d7ca8aed 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -39,6 +39,20 @@ static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; static constexpr char InfoIcon[] = "objlist_info"; +struct InfoItemAtributes { + std::string name; + std::string bmp_name; +}; + +const std::map INFO_ITEMS{ +// info_item Type info_item Name info_item BitmapName + { InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports" }, }, + { InfoItemType::CustomSeam, {L("Paint-on seam"), "seam" }, }, + { InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation"}, }, + { InfoItemType::Sinking, {L("Sinking"), "support_blocker"}, }, + { InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, }, +}; + ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, @@ -60,14 +74,10 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* pare ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) : m_parent(parent), m_type(itInfo), - m_extruder(wxEmptyString) + m_info_item_type(info_type), + m_extruder(wxEmptyString), + m_name(_(INFO_ITEMS.at(info_type).name)) { - m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports") : - info_type == InfoItemType::CustomSeam ? _L("Paint-on seam") : - info_type == InfoItemType::MmuSegmentation ? _L("Multimaterial painting") : - info_type == InfoItemType::Sinking ? _L("Sinking") : - _L("Variable layer height"); - m_info_item_type = info_type; } @@ -307,7 +317,8 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); - m_info_bmp = create_scaled_bitmap(InfoIcon); + for (auto item : INFO_ITEMS) + m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); } ObjectDataViewModel::~ObjectDataViewModel() @@ -402,7 +413,7 @@ wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_it } root->Insert(node, idx+1); - node->SetBitmap(m_info_bmp); + node->SetBitmap(m_info_bmps.at(info_type)); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index b1330f93d6..099dfb7902 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -251,8 +251,8 @@ class ObjectDataViewModel :public wxDataViewModel { std::vector m_objects; std::vector m_volume_bmps; + std::map m_info_bmps; wxBitmap m_warning_bmp; - wxBitmap m_info_bmp; wxDataViewCtrl* m_ctrl { nullptr }; From ac86c7c022dca5a9eb36819c0bd7dc2ac6e4ece2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 15:23:31 +0200 Subject: [PATCH 091/240] ObjectList: Update InfoItems after set some instance as a separate object --- src/slic3r/GUI/GUI_ObjectList.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 05e9f252c9..07119b8de1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3857,6 +3857,7 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set // update printable state for new volumes on canvas3D wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(new_obj_indx); + update_info_items(new_obj_indx); } void ObjectList::instances_to_separated_objects(const int obj_idx) @@ -3889,6 +3890,8 @@ void ObjectList::instances_to_separated_objects(const int obj_idx) // update printable state for new volumes on canvas3D wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(object_idxs); + for (size_t object : object_idxs) + update_info_items(object); } void ObjectList::split_instances() From e947a29fc88f098febd2d93a8d9acf8ccedd4229 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 12 Aug 2021 15:27:32 +0200 Subject: [PATCH 092/240] Follow-up to 7c01ddf996f4b8ca6d7e71c001e7650b3bf14609 1) Starting with this commit, configuration block exported into G-code is delimited by "; prusaslicer_config = begin" and "; prusaslicer_config = end". These delimiters look like any other key / value configuration pairs on purpose to be compatible with older PrusaSlicer config parsing from G-code. 2) Config parser from G-code newly searches for "; generated by ..." comment over the complete G-code, thus it is compatible with various post processing scripts extending the G-code at the start. 3) Config parser from G-code parses PrusaSlicer version from the "; generated by PrusaSlicer ...." header and if the G-code was generated by PrusaSlicer 2.4.0-alpha0 and newer, it expects that the G-code already contains the "; prusaslicer_config = begin / end" tags and it relies on these tags to extract configuration. 4) A new simple and robust parser was written for reading project configuration from 3MF / AMF, while a heuristic parser to read config from G-code located at the end of the G-code file was used before. --- src/libslic3r/Config.cpp | 255 +++++++++++++++++++++---- src/libslic3r/Config.hpp | 8 +- src/libslic3r/Format/3mf.cpp | 4 +- src/libslic3r/Format/AMF.cpp | 4 +- src/libslic3r/GCode.cpp | 6 +- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- src/libslic3r/PresetBundle.cpp | 2 +- src/libslic3r/Semver.hpp | 15 +- 8 files changed, 251 insertions(+), 45 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 8fb774cb92..41ee9231f9 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -625,7 +625,7 @@ void ConfigBase::setenv_() const ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) { return is_gcode_file(file) ? - this->load_from_gcode_file(file, true /* check header */, compatibility_rule) : + this->load_from_gcode_file(file, compatibility_rule) : this->load_from_ini(file, compatibility_rule); } @@ -637,6 +637,54 @@ ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCo return this->load(tree, compatibility_rule); } +ConfigSubstitutions ConfigBase::load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + boost::property_tree::ptree tree; + std::istringstream iss(data); + boost::property_tree::read_ini(iss, tree); + return this->load(tree, compatibility_rule); +} + +// Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF. +// Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). +ConfigSubstitutions ConfigBase::load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + // Convert the "data" string into INI format by removing the semi-colons at the start of a line. + // Also the "; generated by PrusaSlicer ..." comment line will be removed. + size_t j = 0; + for (size_t i = 0; i < data.size();) + if (i == 0 || data[i] == '\n') { + // Start of a line. + if (i != 0) { + // Consume LF. + assert(data[i] == '\n'); + // Don't keep empty lines. + if (j != 0 && data[j] != '\n') + data[j ++] = data[i ++]; + } + // Skip all leading spaces; + for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++ i) ; + // Skip the semicolon (comment indicator). + if (i < data.size() && data[i] == ';') + ++ i; + // Skip all leading spaces after semicolon. + for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++ i) ; + if (strncmp(data.data() + i, "generated by ", 13) == 0) { + // Skip the "; generated by ..." line. + for (; i < data.size() && data[i] != '\n'; ++ i); + } + } else if (data[i] == '\r' && i + 1 < data.size() && data[i + 1] == '\n') { + // Skip CR. + ++ i; + } else { + // Consume the rest of the data. + data[j ++] = data[i ++]; + } + data.erase(data.begin() + j, data.end()); + + return this->load_from_ini_string(data, compatibility_rule); +} + ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule) { ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); @@ -651,37 +699,8 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo return std::move(substitutions_ctxt.substitutions); } -// Load the config keys from the tail of a G-code file. -ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, bool check_header, ForwardCompatibilitySubstitutionRule compatibility_rule) -{ - // Read a 64k block from the end of the G-code. - boost::nowide::ifstream ifs(file); - if (check_header) { - const char slic3r_gcode_header[] = "; generated by Slic3r "; - const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; - std::string firstline; - std::getline(ifs, firstline); - if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && - strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) - throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code."); - } - ifs.seekg(0, ifs.end); - auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length); - ifs.seekg(file_length - data_length, ifs.beg); - std::vector data(size_t(data_length) + 1, 0); - ifs.read(data.data(), data_length); - ifs.close(); - - ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); - size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt); - if (key_value_pairs < 80) - throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); - return std::move(substitutions_ctxt.substitutions); -} - // Load the config keys from the given string. -size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions) +static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions) { if (str == nullptr) return 0; @@ -704,7 +723,7 @@ size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionCon if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ') break; const char *key = start + 2; - if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')) + if (!((*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))) // A key must start with a letter. break; const char *sep = key; @@ -726,7 +745,7 @@ size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionCon if (key == nullptr) break; try { - this->set_deserialize(std::string(key, key_end), std::string(value, end), substitutions); + config.set_deserialize(std::string(key, key_end), std::string(value, end), substitutions); ++num_key_value_pairs; } catch (UnknownOptionException & /* e */) { @@ -735,7 +754,175 @@ size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionCon end = start; } - return num_key_value_pairs; + return num_key_value_pairs; +} + +// Reading a config from G-code back to front for performance reasons: We don't want to scan +// hundreds of MB file for a short config block, which we expect to find at the end of the G-code. +class ReverseLineReader +{ +public: + using pos_type = boost::nowide::ifstream::pos_type; + + // Stop at file_start + ReverseLineReader(boost::nowide::ifstream &ifs, pos_type file_start) : m_ifs(ifs), m_file_start(file_start) + { + m_ifs.seekg(0, m_ifs.end); + m_file_pos = m_ifs.tellg(); + m_block.assign(m_block_size, 0); + } + + bool getline(std::string &out) { + out.clear(); + for (;;) { + if (m_block_len == 0) { + // Read the next block. + m_block_len = size_t(std::min(m_block_size, m_file_pos - m_file_start)); + if (m_block_len == 0) + return false; + m_file_pos -= m_block_len; + m_ifs.seekg(m_file_pos, m_ifs.beg); + if (! m_ifs.read(m_block.data(), m_block_len)) + return false; + } + + assert(m_block_len > 0); + // Non-empty buffer. Find another LF. + int i = int(m_block_len) - 1; + for (; i >= 0; -- i) + if (m_block[i] == '\n') + break; + // i is position of LF or -1 if not found. + if (i == -1) { + // LF not found. Just make a backup of the buffer and continue. + out.insert(out.begin(), m_block.begin(), m_block.begin() + m_block_len); + m_block_len = 0; + } else { + assert(i >= 0); + // Copy new line to the output. It may be empty. + out.insert(out.begin(), m_block.begin() + i + 1, m_block.begin() + m_block_len); + // Block length without the newline. + m_block_len = i; + // Remove CRLF from the end of the block. + if (m_block_len > 0 && m_block[m_block_len - 1] == '\r') + -- m_block_len; + return true; + } + } + assert(false); + return false; + } + +private: + boost::nowide::ifstream &m_ifs; + std::vector m_block; + size_t m_block_size = 65536; + size_t m_block_len = 0; + pos_type m_file_start; + pos_type m_file_pos = 0; +}; + +// Load the config keys from the tail of a G-code file. +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + // Read a 64k block from the end of the G-code. + boost::nowide::ifstream ifs(file); + // Look for Slic3r or PrusaSlicer header. + // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user. + bool has_delimiters = false; + { + static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; + static constexpr const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; + std::string header; + bool header_found = false; + while (std::getline(ifs, header)) { + if (strncmp(slic3r_gcode_header, header.c_str(), strlen(slic3r_gcode_header)) == 0) { + header_found = true; + break; + } else if (strncmp(prusaslicer_gcode_header, header.c_str(), strlen(prusaslicer_gcode_header)) == 0) { + // Parse PrusaSlicer version. + size_t i = strlen(prusaslicer_gcode_header); + for (; i < header.size() && header[i] == ' '; ++ i) ; + size_t j = i; + for (; j < header.size() && header[j] != ' '; ++ j) ; + try { + Semver semver(header.substr(i, j - i)); + has_delimiters = semver >= Semver(2, 4, 0, nullptr, "alpha0"); + } catch (const RuntimeError &) { + } + header_found = true; + break; + } + } + if (! header_found) + throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code."); + } + + auto header_end_pos = ifs.tellg(); + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + size_t key_value_pairs = 0; + + if (has_delimiters) + { + // PrusaSlicer starting with 2.4.0-alpha0 delimits the config section stored into G-code with + // ; prusaslicer_config = begin + // ... + // ; prusaslicer_config = end + // The begin / end tags look like any other key / value pairs on purpose to be compatible with older G-code viewer. + // Read the file in reverse line by line. + ReverseLineReader reader(ifs, header_end_pos); + // Read the G-code file by 64k blocks back to front. + bool begin_found = false; + bool end_found = false; + std::string line; + while (reader.getline(line)) + if (line == "; prusaslicer_config = end") { + end_found = true; + break; + } + if (! end_found) + throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", file)); + std::string key, value; + while (reader.getline(line)) { + if (line == "; prusaslicer_config = begin") { + begin_found = true; + break; + } + // line should be a valid key = value pair. + auto pos = line.find('='); + if (pos != std::string::npos && pos > 1 && line.front() == ';') { + key = line.substr(1, pos - 1); + value = line.substr(pos + 1); + boost::trim(key); + boost::trim(value); + try { + this->set_deserialize(key, value, substitutions_ctxt); + ++ key_value_pairs; + } catch (UnknownOptionException & /* e */) { + // ignore + } + } + } + if (! begin_found) + throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", file)); + } + else + { + // Slic3r or PrusaSlicer older than 2.4.0-alpha0 do not emit any delimiter. + // Try a heuristics reading the G-code from back. + ifs.seekg(0, ifs.end); + auto file_length = ifs.tellg(); + auto data_length = std::min(65535, file_length - header_end_pos); + ifs.seekg(file_length - data_length, ifs.beg); + std::vector data(size_t(data_length) + 1, 0); + ifs.read(data.data(), data_length); + ifs.close(); + key_value_pairs = load_from_gcode_string_legacy(*this, data.data(), substitutions_ctxt); + } + + if (key_value_pairs < 80) + throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + return std::move(substitutions_ctxt.substitutions); } void ConfigBase::save(const std::string &file) const diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 04355bd277..8690f2eb52 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1934,9 +1934,11 @@ public: void setenv_() const; ConfigSubstitutions load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); ConfigSubstitutions load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); - ConfigSubstitutions load_from_gcode_file(const std::string &file, bool check_header /* = true */, ForwardCompatibilitySubstitutionRule compatibility_rule); - // Returns number of key/value pairs extracted. - size_t load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions); + ConfigSubstitutions load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule); + // Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF. + // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). + ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule); + ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); void save(const std::string &file) const; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 16d86ac284..2a76f218fb 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -875,7 +875,9 @@ namespace Slic3r { add_error("Error while reading config data to buffer"); return; } - config.load_from_gcode_string(buffer.data(), config_substitutions); + //FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment. + // Each config line is prefixed with a semicolon (G-code comment), that is ugly. + config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule); } } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 0312c7f225..35b3e0cf43 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -706,7 +706,9 @@ void AMFParserContext::endElement(const char * /* name */) case NODE_TYPE_METADATA: if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) { - m_config->load_from_gcode_string(m_value[1].c_str(), *m_config_substitutions); + //FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment. + // Each config line is prefixed with a semicolon (G-code comment), that is ugly. + m_config_substitutions->substitutions = m_config->load_from_ini_string_commented(std::move(m_value[1].c_str()), m_config_substitutions->rule); } else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { const char *opt_key = m_value[0].c_str() + 7; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index abdbafd31f..254f1d4fd9 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1464,13 +1464,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); - // Append full config. - _write(file, "\n"); + // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. + // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. { + _write(file, "\n; prusaslicer_config = begin\n"); std::string full_config; append_full_config(print, full_config); if (!full_config.empty()) _write(file, full_config); + _write(file, "; prusaslicer_config = end\n"); } print.throw_if_canceled(); } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1141ca2a73..b5c10823e4 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1172,7 +1172,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr // Silently substitute unknown values by new ones for loading configurations from PrusaSlicer's own G-code. // Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config, // thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways. - config.load_from_gcode_file(filename, false, ForwardCompatibilitySubstitutionRule::EnableSilent); + config.load_from_gcode_file(filename, ForwardCompatibilitySubstitutionRule::EnableSilent); apply_config(config); } else if (m_producer == EProducer::Simplify3D) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index b2e35fa2ed..9f089ea1da 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -700,7 +700,7 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw if (is_gcode_file(path)) { DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); - ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, true /* check_header */, compatibility_rule); + ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule); Preset::normalize(config); load_config_file_config(path, true, std::move(config)); return config_substitutions; diff --git a/src/libslic3r/Semver.hpp b/src/libslic3r/Semver.hpp index f55fa9f9f4..45d2bac1c4 100644 --- a/src/libslic3r/Semver.hpp +++ b/src/libslic3r/Semver.hpp @@ -25,8 +25,17 @@ public: Semver() : ver(semver_zero()) {} Semver(int major, int minor, int patch, - boost::optional metadata = boost::none, - boost::optional prerelease = boost::none) + boost::optional metadata, boost::optional prerelease) + : ver(semver_zero()) + { + ver.major = major; + ver.minor = minor; + ver.patch = patch; + set_metadata(metadata); + set_prerelease(prerelease); + } + + Semver(int major, int minor, int patch, const char *metadata = nullptr, const char *prerelease = nullptr) : ver(semver_zero()) { ver.major = major; @@ -102,7 +111,9 @@ public: void set_min(int min) { ver.minor = min; } void set_patch(int patch) { ver.patch = patch; } void set_metadata(boost::optional meta) { ver.metadata = meta ? strdup(*meta) : nullptr; } + void set_metadata(const char *meta) { ver.metadata = meta ? strdup(meta) : nullptr; } void set_prerelease(boost::optional pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; } + void set_prerelease(const char *pre) { ver.prerelease = pre ? strdup(pre) : nullptr; } // Comparison bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; } From 117485c5626b45028565e4404f324d3b3f718152 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 15:29:14 +0200 Subject: [PATCH 093/240] Notifications: MMU segmentation icon --- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 01b0144417..fa9845c5d7 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -61,7 +61,7 @@ static const std::map font_icons_large = { {ImGui::SinkingObjectMarker , "move" }, {ImGui::CustomSupportsMarker , "fdm_supports" }, {ImGui::CustomSeamMarker , "seam" }, - {ImGui::MmuSegmentationMarker , "fdm_supports" }, + {ImGui::MmuSegmentationMarker , "mmu_segmentation" }, {ImGui::VarLayerHeightMarker , "layers" }, {ImGui::DocumentationButton , "notification_documentation" }, {ImGui::DocumentationHoverButton, "notification_documentation_hover"}, From a6b083d76aa77acec6a5bc12bd86a150aea7a547 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 12 Aug 2021 14:52:42 +0200 Subject: [PATCH 094/240] Toolbar highlight arrow --- resources/icons/toolbar_arrow.png | Bin 4444 -> 3996 bytes resources/icons/toolbar_arrow.svg | 98 ++++++------------------------ 2 files changed, 20 insertions(+), 78 deletions(-) diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png index 65905a7270141440d91cc912ff82370bf4ef240d..90ddce38f0c0dfa71dc4d6abdf07fd8a74b43a01 100644 GIT binary patch literal 3996 zcmeH~_fr$l(uPBkUM@x=0)nE1B2@^80#bqq1}s!5p@+~>S`1QyXy{6nKqw+D^cp~- zp$k%l&1qMW`UIEH8%AeM15E&+GM@d|?!tM`>izJ!4*@QJl%X8?7W?1yk0uvHdOfm0B#OFsHQOrzMjeMZDn$$ zv%dky>G7&&+~66LexkwM&~JrZ4vbDrzEGa5n=39XI9FeafNVH)L=Vq}L1CVj@$VWl)<-AP!f9WQ9LIh~qq=TTVc7q{{1<=Ak= zH+f4_4*z;V?8~q5?Je^cvz-yous8#IhI?dG_ej~QRQoKn93dD=QdIeRBA#ge&2Z~? zUl%J;h4N^wW6;nuN1{43)7Gr0|D*j=M-X)JX)YCr)RIqxhlU;+4~a5P8=H-lR|yiGiyFw zwN;Ta`r125F*cvg2!bZ_Iz!j9T;f#Uo$W@|uXbq6vHKNZKGqKS*k=|2yBXK#j!7Ls zaWAAABDA@_i>mBNCivcd3Gp=i^fHHbD+rt@NbH5?z#Kmac;y6n*ym&*G9<;QgTenf z4R_lfnOpo|tUE7g1d_s};>J-NHi%i`wSsxdQ?Az+b{Nai$Fw&=%eDbM@4;*-5BuT; z!*QGFB5`nnr7m5DyJVD;ecuQ{xj*Z)4^;nzzT8UQ&r2*@h&)Sz8@wlO=`}J5uY8^* zBLV{rN7c6ezOfdMrQv|74sHi5z-V29wqDm1T#|>gfP)13m!hZj-tn%5Y)jnI6>v)T z%}XVT;3N+JgmOc_+F&x&E@L2;W8jbrD4m_iN+ouOE~57rs3XI#S^(c~v^M6$AtMGh|2GiFw}#-GIkJg#7udW+3JV<*6Py+}um+ z`vbzBk`eNJYsjXmcepD}bQFllthhIPVj^X z!H*_F-I@0$SpwrcQT&(8Q^Rz(a*fT7I$@WlbgEXf83fl$TI|0ODjAjhwVMS_HQF;z zC>^9WYm`a;uU`8VSFvr`yNqHeblERLK{A+uh45Es546&D#)8&q4Lhsi1}Pv z3~m*&poae+gV!Dhk3cQmN6XTwzMDSfxD4mZJic4Mt!NX{>+i+NS&%IA|@%GP@P-33?IKy3;E%&3* z(n;e2>x!xgm#u<0j4`c_JJ}@YM?7|;Mu!I(3>i}j1pY%@x6x=aBMk0+xn!4q+ zCj2u~W>PAfk|$9us!;B6o7mTCUZeER*8JD7z}b6=_-@Y&z?msV&WB@o+k#3+gUHB; z#dZY4*AHJW3r8dqGwq7k04qgLjCxGKv3i=}tcPzHoM(i8?OGHSu(jXwzapaeia);eACH@i8y^ z-iii-C_POzv^{r!IdCQvKE@Zzt0rHScORj@sUaAffo(s2rW2ik+i;h5H86x^S8fR2 z;kF-KS>Y-jImtnmt;jY4^Xl_N9CdRCvC3KD(vZGs) zj+|PH*d4(u~o`UMuHHuV0?L4{T1oMiKijdN;T&Uv6ZmyE3vV%>$R0@A#4Ar<{WPQ}$D z8ybh64+}(WI?YaNuyuTegns<_wnQSTk$$?eTH{itkRMR34S* zn5q+0)b)jy4s@<9Q3pfFjmZ=5+88B`qf8_|pO$iEl(rgrQ>da-KFFvF(~9 zqpa+87+3Lr+6g^Fu!Qh}^h->Fpn=Iv=}}+dC6zDmdzuz>$iUpw8#Bl?eP z8R~AX|FFB%1RSY;i#l!^N#VlStk0YU<+w!n+vH4 zUp30~5LO;!TP{;tp)+qLM7WQtIQ#qfQU$=NnNe(D{-fC-@sCU$9v8m>SBigp_V90% zv#1phJ=zwHMwZI{kiA)WjrTH)!)ZGGEzVlV%32bc%Nl9N3DQrYzZ*gr*|72`O?iuU zP`px}YpkA z6q+tzlq3~F2(SLUKfPB+1}@hJP1ap2ZgkLYW{967eW5_(XyfpZ28);2VqujT(n0+n z+wpbD1`9@w>5D7T&#j^N;l(n13*p_aXxA^v5|SjkVc?9T)S3B->uj`}4f44=`(NL} zL6gc;yN|D2?iBW+k8qERv9gT78PeXWw>Eok5!R@Oq8(N;&I# zSczUmw4aA|$ZP=E^r{UjxTW`JIT>@H45siM4n`t>nDq<&$+WdV!Z(KpBa~Km61!nP zSQ{4?1!C1(0>ZIDsU?qRiy8%g26eY{bv{*CE`oxOm~7a<+qYI~_C&8cX);Km_EWVE zFHEz6zjW>_HyP|opmoI4x7MeZ+{n=v_$WZ^;I(F|JSy>a% z7e%pT;GNcZ8;|?^5V15Nw*<`bjksGuM&x$?Q7c#OG%Mh6hHW_heMz#qK(v$PPlbyI z2%_xiEud>;`X$)BvpzDY8N2X%F(*G_-AjzX0=bpO(#(H@yr5ou(Qj|OVZn;d+|boR zt~ZT^*r0;)5ZtN!D=HUpLaizXNQptD)4J}r4w@)#J8lMw{;-w;_^JF2I6>oTuDQbH zG;LTuYfhd#06yV{JeY%=d3Z>jn;+@UD}+uFipxDqG)gS|YQ0mz1B*xp!?ADSzuH9T zM*Zv%qGo^;plP^EP6O^uJZ-sl$wh%ip;Cv1>rWnb6qEJkG^cOPFd9FdJ_A^Y3LK3d zpE*A2afB1(${#0RKI2wkbtB?fqc2nb?mp%RKkPiCjNO_@! zA_Ac!T?j}=2%$z=zPx|n`wM>7i>{O+!}i~Z8->Yv-B<~so^Bvp(IcLQ z#__*Q4xBF~A~TAiO1f^avEl=d(E|p_I=<0DzMhGScSi5(a2+ORO*6rvgRLF{hBg2E zL|fuWlr!XSG<8Tc@u-2+dOEL0f(R}WhfgZ@#y@c11Ca7422raPI{H`*8wdM}KyUK%I1O#+(jJsmyzA23m6rH&4i7?UiRS8yd2{D%?BMc@n&6|+aRc?T^v^MJFWa1O1ACC!H}~e{1P4H#mDZ1q zeXIsb%*5X}uz(0t-eZjYP%m7yVHN^PJX4^PQa@c$t2!fS1X z*fU|9@{fh(guAx<3%y?N9alhyA%L)=tjxkp;vV_^wd^`u^+dJ~Jj?duT-#<&IMq>Q z$UdIf_w9#@gZPv^&*X^c78uL{Eet*am&VvA{7Q;+RGGLdBNz*>k6&b-w!9U$rT0#9@Z90yGChEJXC0Ew?rOf4oZjjqDbV`oElkf-XtR=MXo zj?^<3y9hiRP1&x zK1+jg`Kgq7o2jt`Y>-i#Cw?MUqWhlJDWg*B@?+l4h5GZmS%*w*2lE;d_s$Bjnv{*c zfQ#$(k@NCD+K@6Lr<&dc+~06LlGMqgwJIVwB?l4_7tcraD&XEJY|lq~EHA7byJ$u=}5+T06JTwWo@ z%ybvS1}+qXh~w|Aku}e?`6beNAB}a6X9dEx1Y8d3Fl`~UOQL#`PUOm1BV$JkC0Q@eD`w<|GrO_b8i)v z;1=^-+}FvBHv1KK#kfixu&S^A@q{6hUtbrK{^?a%8^tPlj<}C!9N(#bcr^w8Bka$6 zQ|#G`-Lg4`Atl9WFDu_r%j)|q^a(25L;>mZKfyC%%yYiR>~f7`2BKkVNtvkOBLU-1 zOnRpwIibw@;5pG&EKQ`*t-SJ!nuxZ9#p)?h3oPN{>V0KiXH5>cu;>Bvy}&P#dEO1N zP5$d=wQqK7oUOUe%`4tOveo(*OM9i0p%k^V_?0URngqJ3t}~!QBSR#?NBX$3spFfK ze?EW2$BZiv{Z?PUTrRI)fEsfkJBD`;A_t9EC9S>14nhd-9159xl`QQEGwWRK@xOQH z8I+aYB@yRPi+2@?`zM=dSd_;+^0b<<^JMG8>qLblLvtzW_oK`XBPfG*dn%F3pjV{b z*CoJSGa~uqbRNsR;9j8c!uRw_u!yg!L?A!Bv&a6#VDQPqVMa^K;v;nPUajI~%{eq3 za6mJ)6Q!=8Om?325q81DLp_(I$Ion%)d7@^MhgeK8g*jro3P|8wkWaY?xXBo@ib|< zsP^5Eg}Xk_WD>8GF{+B7KPGPAr6DZ8YMa?a;oPR7Wrbw%G$U?-9D3+4N zsBkhw@n3woAWUtN&4K{^GLoeP1o^UVLTSwpMhnMjyFm^-honKrHiceu8No1pjS>>Yc2 zeYg5nSii05?N;xMEIpfURe9nU)*mlGO>)5~tLl=H7e3E5uigTwVaCaCHMJL}zjy^W zPcSOwPF^w@t6_s%buSdH`X*-?z3Q!Uyd~%2y|Y~uoZWzXrU(m{Q){MLgIY%A@ed^= zLvhZ`uJg7Vr2XUD-taNn4g3%`lFYK7Fd}%!q5Q937h?C{ z-&3um>EGFYkt859(Z}lmZ&+IpBRd_@Z(T$4X8QxNk8Lo*r`7$s6;8?qW8zlr=zcG} zV)wThRe0Oq zM4!L$WWkdD#k{l}w~y5%5G)!h6*QLDwZM*fTPTA;f6EC;FHXFe=+g7IaCT1=cuW4~ z`MJ+8{Aa8t3|%>-bWdO)(X_?{|AQyx<=LoWYuW~~JnB|t^GAm2?jzoBIZ zRsme+Y+x4~BCerKUaMc=pMl(Isio-dEtPYnH3k)rqeG2O4bs?L{=jUW(PXa6=e?@? zVZbYI8zW?w0cc0rxC`>`5Wkjr+Fl+5J#J$M`lf%71@~c1>d*bt)pVe_XR)u<7xw~r zKjA|2zf}KlHEVC6myJtQh;=g3sZ7_LK7)b;t9or!+QIXZ1Wy`VTY|Eh*=*$vxcE)yo zORA4-&}ESZmRxb@_k=t!HW73HvHMt>Z5<~>zjtUa>lf$eK5dk{Hax#>g+GMB&9zhx zyMfK@T*0oS@FeZsMzOozxZf2Ft>Ca@SPwh0M%%Y!5(aMN`6wU z9D6Qt;NgcI1+!)3$8j6qwHb_ZucXRcSH@UwQ}DdPwl+2K`UQX`p2`t7M?*dk9*!*B zwNRyy4Y$7nA@KXhmr1qNzv5C5Idr-JTfyPc*kS>IF{^QSkPhy33PMsro~&5rJsA^I z>HJMHr|R5_M-2~u&#tA5Z8*Ft&`V~Fu0AEcw_;y~!S!rMGo)12=b9XxY7mZ|uZs|p zv*yb~iV^ZKnY9?}=29-HXED{)r2!d;~x)VWvc}U zP9^NP(vVxQdDL{YMedi$1}0vNeM8v@cgL1h2nfbYINEv2((2>r$@|}zl^<*vH7NR* z^B=J69IbmByd$c9owjk)dGxg`q&+N4z`OS5#Oq50-CKZ*bgZ#9-xq<}>oGr-zk#jM z&}wm|{@~1v+a&EI)~W?&-q(Db!6J8<0mc(BJm1ELrAaTImv?9|J7G*RiEpv+&u*B< z1o)LFH4b)ayf@Q&<4xwSGRKuO6SKPM0_GwVGo>#Cv%Lcej=8P7W;!#yw9&xZ2j;RQ zM%H;Y#J_HqD=JFcRlbbZ&Vpp3WuyyMvKSo*RJyIGfSu{kR5We)0Wi`x(W}rwVEzwC CwD1W4 diff --git a/resources/icons/toolbar_arrow.svg b/resources/icons/toolbar_arrow.svg index a1476bcd9e..ba025d74b0 100644 --- a/resources/icons/toolbar_arrow.svg +++ b/resources/icons/toolbar_arrow.svg @@ -1,79 +1,21 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + + + + + + + + + + + From 60e8e78240bdb7bd7806e1dc02fe69c0f74e524b Mon Sep 17 00:00:00 2001 From: mikolaszuza Date: Thu, 12 Aug 2021 17:48:32 +0200 Subject: [PATCH 095/240] Reorderded hints, removed extra space Edited hints.ini to have tips for new features displayed first. Removed extra space from a hint. --- resources/data/hints.ini | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index bfe2782d7b..28a13236d6 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -7,7 +7,7 @@ # text = Headline of hint\nBody of hint. # Headline is divided by new line (\n) from body. # Headline is automaticaly printed as Bold. -# Body can contain bold marks: text to be bold (currently rendered as diffenert color, not bold due to font limitations) +# Body can contain bold marks: text to be bold (currently rendered as different color, not bold due to font limitations) # Body can contain hypertext: hypertext text # Hypertext must be max one per notification and must be closed by # @@ -51,6 +51,45 @@ # if there are both disabled and preferred, only preferred that are not in disabled are valid. +[hint:Fuzzy skin] +text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model. +hypertext_type = settings +hypertext_settings_opt = fuzzy_skin +hypertext_settings_type = 1 +hypertext_settings_category = Layers and perimeters +disabled_tags = SLA + +[hint:Shapes gallery] +text = Shapes gallery\nDid you know that PrusaSlicer has a Shapes Gallery? You can use the included models as modifiers, negative volumes or as printable objects. Right-click the platter and selectAdd Shape - Gallery. +hypertext_type = gallery +disable_modes = simple + +[hint:Auto-arrange settings] +text = Auto-arrange settings\nDid you know that you can right-click theauto-arrange iconto adjust the size of the gap between objects and to allow automatic rotations? +hypertext_type = plater +hypertext_plater_item = arrange + +[hint:Negative volume] +text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in the documentation. (Requires Advanced or Expert mode.) +hypertext_type = link +documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 +disabled_tags = SLA; simple + +[hint:Simplify mesh] +text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation. +hypertext_type = link +documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 + +[hint:Reload from disk] +text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in the documentation. +hypertext_type = link +documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 + +[hint:Hiding sidebar] +text = Hiding sidebar\nDid you know that you can hide the right sidebar using the shortcut Shift+Tab? You can also enable the icon for this from thePreferences. +hypertext_type = preferences +hypertext_preferences_page = 2 + [hint:Perspective camera] text = Perspective camera\nDid you know that you can use the K key to quickly switch between an orthographic and perspective camera? @@ -73,11 +112,6 @@ hypertext_settings_type = 1 hypertext_settings_category = Infill disabled_tags = SLA; simple -[hint:Hiding sidebar] -text = Hiding sidebar\nDid you know that you can hide the right sidebar using the shortcut Shift+Tab? You can also enable the icon for this from thePreferences. -hypertext_type = preferences -hypertext_preferences_page = 2 - [hint:Variable layer height] text = Variable layer height\nDid you know that you can print different regions of your model with a different layer height and smooth the transitions between them? Try theVariable layer height tool.(Not available for SLA printers.) hypertext_type = plater @@ -89,20 +123,10 @@ text = Undo/redo history\nDid you know that you can right-click theundo/redo hypertext_type = plater hypertext_plater_item = undo -[hint:Auto-arrange settings] -text = Auto-arrange settings\nDid you know that you can right-click theauto-arrange iconto adjust the size of the gap between objects and to allow automatic rotations? -hypertext_type = plater -hypertext_plater_item = arrange - -[hint:Reload from disk] -text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in the documentation. -hypertext_type = link -documentation_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 - [hint:Different layer height for each model] text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in the documentation. hypertext_type = link -documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 +documentation_link= https://help.prusa3d.com/en/article/per-model-settings_1674 disabled_tags = SLA [hint:Solid infill threshold area] @@ -114,7 +138,7 @@ hypertext_settings_category = Infill enabled_tags = FFF; expert [hint:Search functionality] -text = Search functionality\n Did you know that you use theSearchtool to quickly find a specific PrusaSlicer setting? Or use the familiar shortcut Ctrl+F. +text = Search functionality\nDid you know that you use theSearchtool to quickly find a specific PrusaSlicer setting? Or use the familiar shortcut Ctrl+F. hypertext_type = plater hypertext_plater_item = search @@ -124,11 +148,6 @@ text = Box selection\nDid you know that you can do a box selection with Shift+Mo [hint:Zoom on selected objects or on all objects if none selected] text =Zoom on selected objects or on all objects if none selected\nDid you know that you can zoom in on selected objects by pressing the Z key? If none are selected, the camera will zoom on all objects in the scene. -[hint:Shapes gallery] -text = Shapes gallery\nDid you know that PrusaSlicer has a Shapes Gallery? You can use the included models as modifiers, negative volumes or as printable objects. Right-click the platter and selectAdd Shape - Gallery. -hypertext_type = gallery -disable_modes = simple - [hint:Printable toggle] text = Printable toggle\nDid you know that you can disable the G-code generation for the selected model without having to move or delete it? Toggle the Printable property of a model from the Right-click context menu. @@ -147,20 +166,6 @@ hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/ironing_177488 disabled_tags = SLA; simple -[hint:Fuzzy skin] -text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model. -hypertext_type = settings -hypertext_settings_opt = fuzzy_skin -hypertext_settings_type = 1 -hypertext_settings_category = Layers and perimeters -disabled_tags = SLA - -[hint:Negative volume] -text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in the documentation. (Requires Advanced or Expert mode.) -hypertext_type = link -documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 -disabled_tags = SLA; simple - [hint:Paint-on supports] text = Paint-on supports\nDid you know that you can paint directly on the object and select areas, where supports should be enforced or blocked? Try thePaint-on supportsfeature. (Requires Advanced or Expert mode.) hypertext_type = gizmo @@ -213,11 +218,6 @@ disabled_tags = SLA text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. enabled_tags = Windows -[hint:Simplify mesh] -text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation. -hypertext_type = link -documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 - #[hint:] #text = #hypertext = From bcad6a251d9e7a7429b9037bcd4d2d779939d3f6 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 13 Aug 2021 00:39:55 +0200 Subject: [PATCH 096/240] Added bed model and bed texture for i3 Mega, i3 Mega S. --- resources/profiles/Anycubic.idx | 2 ++ resources/profiles/Anycubic.ini | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/profiles/Anycubic.idx b/resources/profiles/Anycubic.idx index cc3b55ef4c..a834d2c706 100644 --- a/resources/profiles/Anycubic.idx +++ b/resources/profiles/Anycubic.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.3.2-alpha0 +0.0.11 Added bed model and texture for i3 Mega, i3 Mega S. min_slic3r_version = 2.3.1-beta 0.0.10 Various updates for Anycubic Mega. Added filament profiles. 0.0.9 Updated bed textures diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini index c1b763879c..2cb42f0281 100644 --- a/resources/profiles/Anycubic.ini +++ b/resources/profiles/Anycubic.ini @@ -5,7 +5,7 @@ name = Anycubic # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 0.0.10 +config_version = 0.0.11 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anycubic/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -46,12 +46,16 @@ name = Anycubic i3 Mega variants = 0.4 technology = FFF family = MEGA +bed_model = i3megas_bed.stl +bed_texture = i3megas.svg [printer_model:I3MEGAS] name = Anycubic i3 Mega S variants = 0.4 technology = FFF family = MEGA +bed_model = i3megas_bed.stl +bed_texture = i3megas.svg [printer_model:PREDATOR] name = Anycubic Predator From 8e28ca6f4a86410e6ed0093899d6b857ad5c7035 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 13 Aug 2021 10:52:44 +0200 Subject: [PATCH 097/240] Fixed reading of configuration from 3MF broken with e947a29fc88f098febd2d93a8d9acf8ccedd4229 --- src/libslic3r/Config.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 41ee9231f9..b7facbe0e8 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -659,8 +659,9 @@ ConfigSubstitutions ConfigBase::load_from_ini_string_commented(std::string &&dat // Consume LF. assert(data[i] == '\n'); // Don't keep empty lines. - if (j != 0 && data[j] != '\n') - data[j ++] = data[i ++]; + if (j > 0 && data[j - 1] != '\n') + data[j ++] = data[i]; + ++ i; } // Skip all leading spaces; for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++ i) ; From 88c8134f156a42aacbdab5397b160cd95f346b40 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 13 Aug 2021 11:00:30 +0200 Subject: [PATCH 098/240] AutoColorChange detection: Small improvements - at least 25% (instead of 30%) of object's height have to be a solid --- src/slic3r/GUI/GUI_Preview.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d87c23d81e..5f9ad5ba51 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -709,9 +709,9 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee const ExPolygons& bottom = object->get_layer(0)->lslices; double bottom_area = area(bottom); - // at least 30% of object's height have to be a solid - int i; - for (i = 1; i < int(0.3 * num_layers); ++ i) { + // at least 25% of object's height have to be a solid + int i, min_solid_height = int(0.25 * num_layers); + for (i = 1; i <= min_solid_height; ++ i) { double cur_area = area(object->get_layer(i)->lslices); if (cur_area != bottom_area && fabs(cur_area - bottom_area) > scale_(scale_(1))) { // but due to the elephant foot compensation, the first layer may be slightly smaller than the others @@ -723,7 +723,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee break; } } - if (i < int(0.3 * num_layers)) + if (i < min_solid_height) continue; // bottom layer have to be a biggest, so control relation between bottom layer and object size From 731ac9287ec36068bb301c6943900d9681281243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 13 Aug 2021 12:00:55 +0200 Subject: [PATCH 099/240] Added a missing include (GCC 11.1) --- src/slic3r/GUI/ObjectDataViewModel.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 099dfb7902..86e64a8540 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "ExtraRenderers.hpp" From 1e60acde12ac98f358959d25867be1d9e9560bee Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 28 Jun 2021 17:20:04 +0200 Subject: [PATCH 100/240] Substitutions : Implemented InfoDialog --- resources/icons/white/info.svg | 71 +++++++++++++++++++++++ src/libslic3r/Utils.hpp | 2 +- src/libslic3r/utils.cpp | 6 +- src/slic3r/GUI/GUI.cpp | 86 ++++++++++++++++++++++++++++ src/slic3r/GUI/GUI.hpp | 3 + src/slic3r/GUI/GUI_App.cpp | 17 ++---- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 16 ++---- src/slic3r/GUI/MsgDialog.cpp | 38 ++++++++++++ src/slic3r/GUI/Plater.cpp | 8 +-- 10 files changed, 214 insertions(+), 35 deletions(-) create mode 100644 resources/icons/white/info.svg diff --git a/resources/icons/white/info.svg b/resources/icons/white/info.svg new file mode 100644 index 0000000000..db227aa320 --- /dev/null +++ b/resources/icons/white/info.svg @@ -0,0 +1,71 @@ + +image/svg+xml + + + + + + + + + + diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 2ae726b971..1b02e03310 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -238,7 +238,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER return container[next_idx_modulo(idx, container.size())]; } -extern std::string xml_escape(std::string text); +extern std::string xml_escape(std::string text, bool is_marked = false); #if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__ diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 3c2a0810ba..29f955e924 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -888,7 +888,7 @@ unsigned get_current_pid() #endif } -std::string xml_escape(std::string text) +std::string xml_escape(std::string text, bool is_marked/* = false*/) { std::string::size_type pos = 0; for (;;) @@ -903,8 +903,8 @@ std::string xml_escape(std::string text) case '\"': replacement = """; break; case '\'': replacement = "'"; break; case '&': replacement = "&"; break; - case '<': replacement = "<"; break; - case '>': replacement = ">"; break; + case '<': replacement = is_marked ? "<" :"<"; break; + case '>': replacement = is_marked ? ">" :">"; break; default: break; } diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 40c1d52676..15162855c8 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -244,6 +244,92 @@ void warning_catcher(wxWindow* parent, const wxString& message) msg.ShowModal(); } +static void add_config_substitutions(const ConfigSubstitutions& conf_substitutions, wxString& changes) +{ + for (const ConfigSubstitution& conf_substitution : conf_substitutions) { + wxString new_val; + if (!conf_substitution.opt_def) + continue; + if (conf_substitution.opt_def->type == coEnum) { + const std::vector& labels = conf_substitution.opt_def->enum_labels; + const std::vector& values = conf_substitution.opt_def->enum_values; + int val = conf_substitution.new_value->getInt(); + + bool is_infill = conf_substitution.opt_def->opt_key == "top_fill_pattern" || + conf_substitution.opt_def->opt_key == "bottom_fill_pattern" || + conf_substitution.opt_def->opt_key == "fill_pattern"; + + // Each infill doesn't use all list of infill declared in PrintConfig.hpp. + // So we should "convert" val to the correct one + if (is_infill) { + for (const auto& key_val : *conf_substitution.opt_def->enum_keys_map) + if ((int)key_val.second == val) { + auto it = std::find(values.begin(), values.end(), key_val.first); + if (it == values.end()) + break; + new_val = from_u8(_utf8(labels[it - values.begin()])); + break; + } + new_val = _L("Undef"); + } + else + new_val = from_u8(_utf8(labels[val])); + } + else if (conf_substitution.opt_def->type == coBool) + new_val = conf_substitution.new_value->getBool() ? "true" : "false"; + + changes += "\n" + GUI::format(_L("New unknown value \"%1%\" was changed to defaul value \"%2%\""), conf_substitution.old_value, new_val); + } +} + +void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions) +{ + wxString changes; + + auto preset_type_name = [](Preset::Type type) { + return type == Slic3r::Preset::TYPE_PRINT ? _L("Print") : + type == Slic3r::Preset::TYPE_SLA_PRINT ? _L("SLA Print") : + type == Slic3r::Preset::TYPE_FILAMENT ? _L("Filament") : + type == Slic3r::Preset::TYPE_SLA_MATERIAL ? _L("SLA Material") : + type == Slic3r::Preset::TYPE_PRINTER ? _L("Printer") : ""; + }; + + for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { + changes += "\n

" + GUI::format(_L(" %1% Preset : %2%"), preset_type_name(substitution.preset_type), substitution.preset_name); + if (!substitution.preset_file.empty()) + changes += GUI::format(" (%1%)", substitution.preset_file); + changes += "

"; + + add_config_substitutions(substitution.substitutions, changes); + } + if (!changes.IsEmpty()) + changes += "\n\n"; + + wxString message = format(_L("Loading profiles found following incompatibilities:%1%" + " To recover these files, incompatible values were changed to default values.\n" + " But data in files won't be changed until you save them in PrusaSlicer."), changes); + + InfoDialog msg(nullptr, message); + msg.ShowModal(); +} + +void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename) +{ + wxString changes = "\n"; + + add_config_substitutions(config_substitutions, changes); + + if (!changes.IsEmpty()) + changes += "\n\n"; + + wxString message = format(_L("Loading %1% file found incompatibilities.\n" + "To recover this file, incompatible values were changed to default values:%2%" + "But data in files won't be changed until you save them in PrusaSlicer."), from_u8(filename), changes); + + InfoDialog msg(nullptr, message); + msg.ShowModal(); +} + void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items) { if (comboCtrl == nullptr) diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index c70dffcc32..20c8828785 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -7,6 +7,7 @@ namespace boost::filesystem { class path; } #include #include "libslic3r/Config.hpp" +#include "libslic3r/Preset.hpp" class wxWindow; class wxMenuBar; @@ -48,6 +49,8 @@ void show_info(wxWindow* parent, const wxString& message, const wxString& title void show_info(wxWindow* parent, const char* message, const char* title = nullptr); inline void show_info(wxWindow* parent, const std::string& message,const std::string& title = std::string()) { show_info(parent, message.c_str(), title.c_str()); } void warning_catcher(wxWindow* parent, const wxString& message); +void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions); +void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename); // Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items. // Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true). diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4fa80bf58a..3205bddca9 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -626,12 +626,8 @@ void GUI_App::post_init() this->plater()->load_gcode(wxString::FromUTF8(this->init_params->input_files[0].c_str())); } else { - if (! this->init_params->preset_substitutions.empty()) { - // TODO: Add list of changes from all_substitutions - show_error(nullptr, GUI::format(_L("Loading profiles found following incompatibilities." - " To recover these files, incompatible values were changed to default values." - " But data in files won't be changed until you save them in PrusaSlicer."))); - } + if (! this->init_params->preset_substitutions.empty()) + show_substitutions_info(this->init_params->preset_substitutions); #if 0 // Load the cummulative config over the currently active profiles. @@ -1879,12 +1875,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) try { app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); if (PresetsConfigSubstitutions all_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable); - ! all_substitutions.empty()) { - // TODO: - show_error(nullptr, GUI::format(_L("Loading profiles found following incompatibilities." - " To recover these files, incompatible values were changed to default values." - " But data in files won't be changed until you save them in PrusaSlicer."))); - } + ! all_substitutions.empty()) + show_substitutions_info(all_substitutions); + // Load the currently selected preset into the GUI, update the preset selection box. load_current_presets(); } catch (std::exception &ex) { diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index f6e3976ea4..e964793193 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -159,7 +159,7 @@ void SLAImportJob::process() } if (! config_substitutions.empty()) { - //FIXME Add reporting here "Loading profiles found following incompatibilities." + show_substitutions_info(config_substitutions, path); } update_status(100, was_canceled() ? _(L("Importing canceled.")) : diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ef1f236a32..4f53d3f013 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1749,12 +1749,8 @@ bool MainFrame::load_config_file(const std::string &path) { try { ConfigSubstitutions config_substitutions = wxGetApp().preset_bundle->load_config_file(path, ForwardCompatibilitySubstitutionRule::Enable); - if (! config_substitutions.empty()) { - // TODO: Add list of changes from all_substitutions - show_error(nullptr, GUI::format(_L("Loading profiles found following incompatibilities." - " To recover these files, incompatible values were changed to default values." - " But data in files won't be changed until you save them in PrusaSlicer."))); - } + if (!config_substitutions.empty()) + show_substitutions_info(config_substitutions, path); } catch (const std::exception &ex) { show_error(this, ex.what()); return false; @@ -1819,12 +1815,8 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re return; } - if (! config_substitutions.empty()) { - // TODO: Add list of changes from all_substitutions - show_error(nullptr, GUI::format(_L("Loading profiles found following incompatibilities." - " To recover these files, incompatible values were changed to default values." - " But data in files won't be changed until you save them in PrusaSlicer."))); - } + if (! config_substitutions.empty()) + show_substitutions_info(config_substitutions); // Load the currently selected preset into the GUI, update the preset selection box. wxGetApp().load_current_presets(); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 28bb9cc93f..ccbdf2e150 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -211,5 +211,43 @@ MessageDialog::MessageDialog(wxWindow* parent, } #endif + +// InfoDialog + +InfoDialog::InfoDialog(wxWindow* parent, const wxString& msg) + : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), _L("Note that")) + , msg(msg) +{ + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + // Text shown as HTML, so that mouse selection and Ctrl-V to copy will work. + wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + { + html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + wxFont monospace = wxGetApp().code_font(); + wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); + auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + const int font_size = font.GetPointSize() - 1; + int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size }; + html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size); + html->SetBorders(2); + std::string msg_escaped = xml_escape(msg.ToUTF8().data(), true); + boost::replace_all(msg_escaped, "\r\n", "
"); + boost::replace_all(msg_escaped, "\n", "
"); + html->SetPage("" + wxString::FromUTF8(msg_escaped.data()) + ""); + content_sizer->Add(html, 1, wxEXPAND); + } + + // Set info bitmap + logo->SetBitmap(create_scaled_bitmap("info", this, 84)); + + SetMinSize(wxSize(60 * wxGetApp().em_unit(), 30 * wxGetApp().em_unit())); + Fit(); +} + + } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b85cc3cd79..c57fffa6e3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2261,12 +2261,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ // and place the loaded config over the base. config += std::move(config_loaded); } - if (! config_substitutions.empty()) { - // TODO: - show_error(nullptr, GUI::format(_L("Loading profiles found following incompatibilities." - " To recover these files, incompatible values were changed to default values." - " But data in files won't be changed until you save them in PrusaSlicer."))); - } + if (! config_substitutions.empty()) + show_substitutions_info(config_substitutions.substitutions, filename.string()); this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; } From 856da036eb0cbd041469ef8a28f0cfb2f1743c3b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 28 Jun 2021 17:26:24 +0200 Subject: [PATCH 101/240] Fixed loading of system presets with incompatible system profile keys before the "reconfigure" dialog is shown. Replaced boost::filesystem::copy_file() with Slic3r::copy_file() in config snapshot loading code. --- src/libslic3r/Config.cpp | 3 +- src/libslic3r/Config.hpp | 7 +++++ src/libslic3r/PresetBundle.cpp | 44 +++++++++++++++++------------- src/libslic3r/PresetBundle.hpp | 8 ++++-- src/slic3r/Config/Snapshot.cpp | 8 ++++-- src/slic3r/GUI/ConfigWizard.cpp | 7 +++-- src/slic3r/GUI/GUI_App.cpp | 8 +++++- src/slic3r/GUI/MainFrame.cpp | 4 ++- src/slic3r/Utils/PresetUpdater.cpp | 10 +++++-- 9 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index b7facbe0e8..ae154bf27b 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -553,7 +553,8 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con opt->set(optdef->default_value.get()); - if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable) { + if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || + substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) { // Log the substitution. ConfigSubstitution config_substitution; config_substitution.opt_def = optdef; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 8690f2eb52..ab3caebb8a 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -166,9 +166,16 @@ enum PrinterTechnology : unsigned char enum ForwardCompatibilitySubstitutionRule { + // Disable susbtitution, throw exception if an option value is not recognized. Disable, + // Enable substitution of an unknown option value with default. Log the substitution. Enable, + // Enable substitution of an unknown option value with default. Don't log the substitution. EnableSilent, + // Enable substitution of an unknown option value with default. Log substitutions in user profiles, don't log substitutions in system profiles. + EnableSystemSilent, + // Enable silent substitution of an unknown option value with default when loading user profiles. Throw on an unknown option value in a system profile. + EnableSilentDisableSystem, }; class ConfigOption; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 9f089ea1da..53743411c2 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -191,7 +191,9 @@ void PresetBundle::setup_directories() PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, const std::string &preferred_model_id) { // First load the vendor specific system presets. - std::string errors_cummulative = this->load_system_presets(); + PresetsConfigSubstitutions substitutions; + std::string errors_cummulative; + std::tie(substitutions, errors_cummulative) = this->load_system_presets(substitution_rule); const std::string dir_user_presets = data_dir() #ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR @@ -202,7 +204,6 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward #endif ; - PresetsConfigSubstitutions substitutions; try { this->prints.load_presets(dir_user_presets, "print", substitutions, substitution_rule); } catch (const std::runtime_error &err) { @@ -245,12 +246,20 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward // Load system presets into this PresetBundle. // For each vendor, there will be a single PresetBundle loaded. -std::string PresetBundle::load_system_presets() +std::pair PresetBundle::load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule) { + if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) + // Loading system presets, don't log substitutions. + compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent; + else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem) + // Loading system presets, throw on unknown option value. + compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable; + // Here the vendor specific read only Config Bundles are stored. - boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred(); - std::string errors_cummulative; - bool first = true; + boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred(); + PresetsConfigSubstitutions substitutions; + std::string errors_cummulative; + bool first = true; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) if (Slic3r::is_ini_file(dir_entry)) { std::string name = dir_entry.path().filename().string(); @@ -260,13 +269,13 @@ std::string PresetBundle::load_system_presets() // Load the config bundle, flatten it. if (first) { // Reset this PresetBundle and load the first vendor config. - this->load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem); + append(substitutions, this->load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem, compatibility_rule).first); first = false; } else { // Load the other vendor configs, merge them with this PresetBundle. // Report duplicate profiles. PresetBundle other; - other.load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem); + append(substitutions, other.load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem, compatibility_rule).first); std::vector duplicates = this->merge_presets(std::move(other)); if (! duplicates.empty()) { errors_cummulative += "Vendor configuration file " + name + " contains the following presets with names used by other vendors: "; @@ -288,7 +297,7 @@ std::string PresetBundle::load_system_presets() } this->update_system_maps(); - return errors_cummulative; + return std::make_pair(std::move(substitutions), errors_cummulative); } // Merge one vendor's presets with the other vendor's presets, report duplicates. @@ -739,7 +748,7 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw return config_substitutions; } case CONFIG_FILE_TYPE_CONFIG_BUNDLE: - return load_config_file_config_bundle(path, tree); + return load_config_file_config_bundle(path, tree, compatibility_rule); } // This shall never happen. Suppres compiler warnings. @@ -916,13 +925,14 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool } // Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. -ConfigSubstitutions PresetBundle::load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree) +ConfigSubstitutions PresetBundle::load_config_file_config_bundle( + const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule) { // 1) Load the config bundle into a temp data. PresetBundle tmp_bundle; // Load the config bundle, but don't save the loaded presets to user profile directory, as only the presets marked as active in the loaded preset bundle // will be loaded into the master PresetBundle and activated. - auto [presets_substitutions, presets_imported] = tmp_bundle.load_configbundle(path, {}); + auto [presets_substitutions, presets_imported] = tmp_bundle.load_configbundle(path, {}, compatibility_rule); UNUSED(presets_imported); std::string bundle_name = std::string(" - ") + boost::filesystem::path(path).filename().string(); @@ -1135,15 +1145,11 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. -std::pair PresetBundle::load_configbundle(const std::string &path, LoadConfigBundleAttributes flags) +std::pair PresetBundle::load_configbundle( + const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule) { // Enable substitutions for user config bundle, throw an exception when loading a system profile. - ConfigSubstitutionContext substitution_context { - flags.has(LoadConfigBundleAttribute::LoadSystem) ? - ForwardCompatibilitySubstitutionRule::Disable : - ForwardCompatibilitySubstitutionRule::Enable - }; - + ConfigSubstitutionContext substitution_context { compatibility_rule }; PresetsConfigSubstitutions substitutions; if (flags.has(LoadConfigBundleAttribute::ResetUserProfile) || flags.has(LoadConfigBundleAttribute::LoadSystem)) diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 9f75ba6c24..3c7349668a 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -102,7 +102,8 @@ public: using LoadConfigBundleAttributes = enum_bitmask; // Load the config bundle based on the flags. // Don't do any config substitutions when loading a system profile, perform and report substitutions otherwise. - std::pair load_configbundle(const std::string &path, LoadConfigBundleAttributes flags); + std::pair load_configbundle( + const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule); // Export a config bundle file containing all the presets and the names of the active presets. void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false); @@ -139,7 +140,7 @@ public: static const char *PRUSA_BUNDLE; private: - std::string load_system_presets(); + std::pair load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule); // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetBundle &&other); // Update renamed_from and alias maps of system profiles. @@ -158,7 +159,8 @@ private: // and the external config is just referenced, not stored into user profile directory. // If it is not an external config, then the config will be stored into the user profile directory. void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); - ConfigSubstitutions load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); + ConfigSubstitutions load_config_file_config_bundle( + const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); DynamicPrintConfig full_fff_config() const; DynamicPrintConfig full_sla_config() const; diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 56722173bc..40aade783d 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -10,6 +10,7 @@ #include #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/format.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/Time.hpp" #include "libslic3r/Config.hpp" @@ -358,11 +359,12 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src { if (! boost::filesystem::is_directory(path_dst) && ! boost::filesystem::create_directory(path_dst)) - throw Slic3r::RuntimeError(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); + throw Slic3r::RuntimeError(std::string("PrusaSlicer was unable to create a directory at ") + path_dst.string()); for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) if (Slic3r::is_ini_file(dir_entry)) - boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); + if (std::string error_message; copy_file(dir_entry.path().string(), (path_dst / dir_entry.path().filename()).string(), error_message, false) != SUCCESS) + throw Slic3r::RuntimeError(format("Failed copying \"%1%\" to \"%2%\": %3%", path_src.string(), path_dst.string(), error_message)); } static void delete_existing_ini_files(const boost::filesystem::path &path) @@ -413,7 +415,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: ++ it; // Read the active config bundle, parse the config version. PresetBundle bundle; - bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly); + bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly, ForwardCompatibilitySubstitutionRule::EnableSilent); for (const auto &vp : bundle.vendors) if (vp.second.id == cfg.name) cfg.version.config_version = vp.second.config_version; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 8bf8cd3d7a..e873d9b07c 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -65,7 +65,9 @@ bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_prusa_bu this->is_prusa_bundle = ais_prusa_bundle; std::string path_string = source_path.string(); - auto [config_substitutions, presets_loaded] = preset_bundle->load_configbundle(path_string, PresetBundle::LoadConfigBundleAttribute::LoadSystem); + // Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air. + auto [config_substitutions, presets_loaded] = preset_bundle->load_configbundle( + path_string, PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::Disable); UNUSED(config_substitutions); // No substitutions shall be reported when loading a system config bundle, no substitutions are allowed. assert(config_substitutions.empty()); @@ -2590,7 +2592,8 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // Reloading the configs after some modifications were done to PrusaSlicer.ini. // Just perform the substitutions silently, as the substitutions were already presented to the user on application start-up // and the Wizard shall not create any new values that would require substitution. - PresetsConfigSubstitutions substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilent, preferred_model); + // Throw on substitutions in system profiles, as the system profiles provided over the air should be compatible with this PrusaSlicer version. + preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, preferred_model); if (page_custom->custom_wanted()) { page_firmware->apply_custom_config(*custom_config); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3205bddca9..6eb0547c32 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -912,7 +912,10 @@ bool GUI_App::on_init_inner() // Suppress the '- default -' presets. preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1"); try { - init_params->preset_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable); + // Enable all substitutions (in both user and system profiles), but log the substitutions in user profiles only. + // If there are substitutions in system profiles, then a "reconfigure" event shall be triggered, which will force + // installation of a compatible system preset, thus nullifying the system preset substitutions. + init_params->preset_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSystemSilent); } catch (const std::exception &ex) { show_error(nullptr, ex.what()); } @@ -1874,6 +1877,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) Config::SnapshotDB::singleton().take_snapshot(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); try { app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); + // Enable substitutions, log both user and system substitutions. There should not be any substitutions performed when loading system + // presets because compatibility of profiles shall be verified using the min_slic3r_version keys in config index, but users + // are known to be creative and mess with the config files in various ways. if (PresetsConfigSubstitutions all_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable); ! all_substitutions.empty()) show_substitutions_info(all_substitutions); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 4f53d3f013..93641adff9 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1809,7 +1809,9 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re size_t presets_imported = 0; PresetsConfigSubstitutions config_substitutions; try { - std::tie(config_substitutions, presets_imported) = wxGetApp().preset_bundle->load_configbundle(file.ToUTF8().data(), PresetBundle::LoadConfigBundleAttribute::SaveImported); + // Report all substitutions. + std::tie(config_substitutions, presets_imported) = wxGetApp().preset_bundle->load_configbundle( + file.ToUTF8().data(), PresetBundle::LoadConfigBundleAttribute::SaveImported, ForwardCompatibilitySubstitutionRule::Enable); } catch (const std::exception &ex) { show_error(this, ex.what()); return; diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 078c2fe208..7fcf1ed684 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -65,6 +65,10 @@ void copy_file_fix(const fs::path &source, const fs::path &target) _L("Copying of file %1% to %2% failed: %3%"), source, target, error_message)); } + // Permissions should be copied from the source file by copy_file(). We are not sure about the source + // permissions, let's rewrite them with 644. + static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; + fs::permissions(target, perms); } struct Update @@ -611,7 +615,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons update.install(); PresetBundle bundle; - bundle.load_configbundle(update.source.string(), PresetBundle::LoadConfigBundleAttribute::LoadSystem); + // Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air. + bundle.load_configbundle(update.source.string(), PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::Disable); BOOST_LOG_TRIVIAL(info) << format("Deleting %1% conflicting presets", bundle.prints.size() + bundle.filaments.size() + bundle.printers.size()); @@ -715,7 +720,8 @@ static void reload_configs_update_gui() auto* app_config = GUI::wxGetApp().app_config; // System profiles should not trigger any substitutions, user profiles may trigger substitutions, but these substitutions // were already presented to the user on application start up. Just do substitutions now and keep quiet about it. - GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilent); + // However throw on substitutions in system profiles, those shall never happen with system profiles installed over the air. + GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem); GUI::wxGetApp().load_current_presets(); GUI::wxGetApp().plater()->set_bed_shape(); } From 5dac5a2ca59148d7b127f3c0a67a5daf11645c6d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 28 Jun 2021 17:45:13 +0200 Subject: [PATCH 102/240] Fixed missing include --- src/slic3r/GUI/GUI.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 15162855c8..616d36ba2b 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -21,6 +21,7 @@ #include "AboutDialog.hpp" #include "MsgDialog.hpp" +#include "format.hpp" #include "libslic3r/Print.hpp" From 4cbe7a9545501a44704869d4b1392b07f8a48de4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 29 Jun 2021 13:25:02 +0200 Subject: [PATCH 103/240] If configuration update is available, show Dialog with information about it before ConfigWizard is opened --- src/slic3r/GUI/GUI_App.cpp | 22 +++++++++++++++++++++- src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/UpdateDialogs.cpp | 21 +++++++++++++++------ src/slic3r/GUI/UpdateDialogs.hpp | 3 ++- src/slic3r/Utils/PresetUpdater.cpp | 16 ++++++++++------ src/slic3r/Utils/PresetUpdater.hpp | 12 +++++++++--- 6 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6eb0547c32..b496c846dc 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1886,6 +1886,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) // Load the currently selected preset into the GUI, update the preset selection box. load_current_presets(); + + // update config wizard in respect to the new config + update_wizard_from_config(); } catch (std::exception &ex) { GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what())); } @@ -2137,6 +2140,17 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/) } } +void GUI_App::update_wizard_from_config() +{ + if (!m_wizard) + return; + // If ConfigWizard was created before changing of the configuration, + // we have to destroy it to have possibility to create it again in respect to the new config's parameters + m_wizard->Reparent(nullptr); + m_wizard->Destroy(); + m_wizard = nullptr; +} + bool GUI_App::OnExceptionInMainLoop() { generic_exception_handle(); @@ -2297,7 +2311,13 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage { wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); + if (reason == ConfigWizard::RR_USER) + if (PresetUpdater::UpdateResult result = preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD); + result == PresetUpdater::R_ALL_CANCELED) + return false; + if (! m_wizard) { + wxBusyCursor wait; m_wizard = new ConfigWizard(mainframe); } @@ -2465,7 +2485,7 @@ void GUI_App::check_updates(const bool verbose) { PresetUpdater::UpdateResult updater_result; try { - updater_result = preset_updater->config_update(app_config->orig_version(), verbose); + updater_result = preset_updater->config_update(app_config->orig_version(), verbose ? PresetUpdater::UpdateParams::SHOW_TEXT_BOX : PresetUpdater::UpdateParams::SHOW_NOTIFICATION); if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { mainframe->Close(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index be6c71f6cc..f23f421332 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -247,6 +247,7 @@ public: bool check_print_host_queue(); bool checked_tab(Tab* tab); void load_current_presets(bool check_printer_presets = true); + void update_wizard_from_config(); wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); } // Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US". diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index c3189c4c11..de132b1848 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -85,8 +85,11 @@ bool MsgUpdateSlic3r::disable_version_check() const // MsgUpdateConfig -MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates) : - MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE) +MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates, bool force_before_wizard/* = false*/) : + MsgDialog(nullptr, force_before_wizard ? _L("Opening Configuration Wizard") : _L("Configuration update"), + force_before_wizard ? _L("PrusaSlicer is not using the newest configuration available.\n" + "Configuration Wizard may not offer the latest printers, filaments and SLA materials to be installed. ") : + _L("Configuration update is available"), wxID_NONE) { auto *text = new wxStaticText(this, wxID_ANY, _(L( "Would you like to install it?\n\n" @@ -130,11 +133,17 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates) : content_sizer->Add(versions); content_sizer->AddSpacer(2*VERT_SPACING); - auto *btn_cancel = new wxButton(this, wxID_CANCEL); - btn_sizer->Add(btn_cancel); - btn_sizer->AddSpacer(HORIZ_SPACING); - auto *btn_ok = new wxButton(this, wxID_OK); + auto* btn_ok = new wxButton(this, wxID_OK, force_before_wizard ? _L("Install") : "OK"); btn_sizer->Add(btn_ok); + btn_sizer->AddSpacer(HORIZ_SPACING); + if (force_before_wizard) { + auto* btn_no_install = new wxButton(this, wxID_ANY, "Don't install"); + btn_no_install->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CLOSE); }); + btn_sizer->Add(btn_no_install); + btn_sizer->AddSpacer(HORIZ_SPACING); + } + auto* btn_cancel = new wxButton(this, wxID_CANCEL); + btn_sizer->Add(btn_cancel); btn_ok->SetFocus(); wxGetApp().UpdateDlgDarkUI(this); diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index 6d355065a1..aa3a10677f 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -54,7 +54,8 @@ public: {} }; - MsgUpdateConfig(const std::vector &updates); + // force_before_wizard - indicates that check of updated is forced before ConfigWizard opening + MsgUpdateConfig(const std::vector &updates, bool force_before_wizard = false); MsgUpdateConfig(MsgUpdateConfig &&) = delete; MsgUpdateConfig(const MsgUpdateConfig &) = delete; MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete; diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 7fcf1ed684..7b4321be99 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -724,9 +724,10 @@ static void reload_configs_update_gui() GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem); GUI::wxGetApp().load_current_presets(); GUI::wxGetApp().plater()->set_bed_shape(); + GUI::wxGetApp().update_wizard_from_config(); } -PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, bool no_notification) const +PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params) const { if (! p->enabled_config_update) { return R_NOOP; } @@ -809,7 +810,11 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 } // regular update - if (no_notification) { + if (params == UpdateParams::SHOW_NOTIFICATION) { + p->set_waiting_updates(updates); + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable); + } + else { BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size()); std::vector updates_msg; @@ -818,7 +823,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); } - GUI::MsgUpdateConfig dlg(updates_msg); + GUI::MsgUpdateConfig dlg(updates_msg, params == UpdateParams::FORCED_BEFORE_WIZARD); const auto res = dlg.ShowModal(); if (res == wxID_OK) { @@ -829,11 +834,10 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 } else { BOOST_LOG_TRIVIAL(info) << "User refused the update"; + if (params == UpdateParams::FORCED_BEFORE_WIZARD && res == wxID_CANCEL) + return R_ALL_CANCELED; return R_UPDATE_REJECT; } - } else { - p->set_waiting_updates(updates); - GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable); } // MsgUpdateConfig will show after the notificaation is clicked diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index 0ca363c613..acec7baf7c 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -35,15 +35,21 @@ public: R_INCOMPAT_CONFIGURED, R_UPDATE_INSTALLED, R_UPDATE_REJECT, - R_UPDATE_NOTIFICATION + R_UPDATE_NOTIFICATION, + R_ALL_CANCELED + }; + + enum class UpdateParams { + SHOW_TEXT_BOX, // force modal textbox + SHOW_NOTIFICATION, // only shows notification + FORCED_BEFORE_WIZARD // indicates that check of updated is forced before ConfigWizard opening }; // If updating is enabled, check if updates are available in cache, if so, ask about installation. // A false return value implies Slic3r should exit due to incompatibility of configuration. // Providing old slic3r version upgrade profiles on upgrade of an application even in case // that the config index installed from the Internet is equal to the index contained in the installation package. - // no_notification = force modal textbox, otherwise some cases only shows notification - UpdateResult config_update(const Semver &old_slic3r_version, bool no_notification) const; + UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params) const; // "Update" a list of bundles from resources (behaves like an online update). void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; From 965c2f2c5592431515fcad4924537539a8c12ecf Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Jun 2021 15:41:47 +0200 Subject: [PATCH 104/240] Improved error reporting when importing various configuration files: 1) Slic3r::RuntimeError was replaced with ConfigurationError, all exceptions thrown by the configuration layer are derived from ConfigurationError. 2) When parsing configuration files, ConfigurationError is catched and rethrown extended with the file name being parsed. --- src/libslic3r/Config.cpp | 57 ++++++++++++--- src/libslic3r/Config.hpp | 125 ++++++++++++++++++-------------- src/libslic3r/PresetBundle.cpp | 106 +++++++++++++++------------ src/slic3r/GUI/Plater.cpp | 4 + tests/libslic3r/test_config.cpp | 2 +- 5 files changed, 181 insertions(+), 113 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index ae154bf27b..e0c639fdf0 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -248,7 +248,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coPercents: return new ConfigOptionPercentsNullable(); case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable(); case coBools: return new ConfigOptionBoolsNullable(); - default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label); + default: throw ConfigurationError(std::string("Unknown option type for nullable option ") + this->label); } } else { switch (this->type) { @@ -269,7 +269,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coBool: return new ConfigOptionBool(); case coBools: return new ConfigOptionBools(); case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); - default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label); + default: throw ConfigurationError(std::string("Unknown option type for option ") + this->label); } } } @@ -497,7 +497,7 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append) { if (! this->set_deserialize_nothrow(opt_key_src, value_src, substitutions_ctxt, append)) - throw BadOptionTypeException(format("ConfigBase::set_deserialize() failed for parameter \"%1%\", value \"%2%\"", opt_key_src, value_src)); + throw BadOptionValueException(format("Invalid value provided for parameter %1%: %2%", opt_key_src, value_src)); } void ConfigBase::set_deserialize(std::initializer_list items, ConfigSubstitutionContext& substitutions_ctxt) @@ -589,7 +589,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const return opt_def->ratio_over.empty() ? 0. : static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over)); } - throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); + throw ConfigurationError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); } // Return an absolute value of a possibly relative config variable. @@ -600,7 +600,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati const ConfigOption *raw_opt = this->option(opt_key); assert(raw_opt != nullptr); if (raw_opt->type() != coFloatOrPercent) - throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); + throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); // Compute absolute value. return static_cast(raw_opt)->get_abs_value(ratio_over); } @@ -632,10 +632,14 @@ ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibili ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) { - boost::property_tree::ptree tree; - boost::nowide::ifstream ifs(file); - boost::property_tree::read_ini(ifs, tree); - return this->load(tree, compatibility_rule); + try { + boost::property_tree::ptree tree; + boost::nowide::ifstream ifs(file); + boost::property_tree::read_ini(ifs, tree); + return this->load(tree, compatibility_rule); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what())); + } } ConfigSubstitutions ConfigBase::load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -701,6 +705,39 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo return std::move(substitutions_ctxt.substitutions); } +// Load the config keys from the tail of a G-code file. +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + try { + // Read a 64k block from the end of the G-code. + boost::nowide::ifstream ifs(file); + { + const char slic3r_gcode_header[] = "; generated by Slic3r "; + const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; + std::string firstline; + std::getline(ifs, firstline); + if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && + strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) + throw ConfigurationError("Not a PrusaSlicer / Slic3r PE generated g-code."); + } + ifs.seekg(0, ifs.end); + auto file_length = ifs.tellg(); + auto data_length = std::min(65535, file_length); + ifs.seekg(file_length - data_length, ifs.beg); + std::vector data(size_t(data_length) + 1, 0); + ifs.read(data.data(), data_length); + ifs.close(); + + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt); + if (key_value_pairs < 80) + throw ConfigurationError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + return std::move(substitutions_ctxt.substitutions); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Failed loading configuration from G-code \"%1%\": %2%", file, e.what())); + } +} + // Load the config keys from the given string. static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions) { @@ -995,7 +1032,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre throw NoDefinitionException(opt_key); const ConfigOptionDef *optdef = def->get(opt_key); if (optdef == nullptr) -// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key); +// throw ConfigurationError(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; ConfigOption *opt = optdef->create_default_option(); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ab3caebb8a..078e94cfb0 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -80,32 +80,47 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< extern std::string escape_ampersand(const std::string& str); -/// Specialization of std::exception to indicate that an unknown config option has been encountered. -class UnknownOptionException : public Slic3r::RuntimeError { +// Base for all exceptions thrown by the configuration layer. +class ConfigurationError : public Slic3r::RuntimeError { public: - UnknownOptionException() : - Slic3r::RuntimeError("Unknown option exception") {} - UnknownOptionException(const std::string &opt_key) : - Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {} + using RuntimeError::RuntimeError; }; -/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). -class NoDefinitionException : public Slic3r::RuntimeError +// Specialization of std::exception to indicate that an unknown config option has been encountered. +class UnknownOptionException : public ConfigurationError { +public: + UnknownOptionException() : + ConfigurationError("Unknown option exception") {} + UnknownOptionException(const std::string &opt_key) : + ConfigurationError(std::string("Unknown option exception: ") + opt_key) {} +}; + +// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). +class NoDefinitionException : public ConfigurationError { public: NoDefinitionException() : - Slic3r::RuntimeError("No definition exception") {} + ConfigurationError("No definition exception") {} NoDefinitionException(const std::string &opt_key) : - Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {} + ConfigurationError(std::string("No definition exception: ") + opt_key) {} }; -/// Indicate that an unsupported accessor was called on a config option. -class BadOptionTypeException : public Slic3r::RuntimeError +// Indicate that an unsupported accessor was called on a config option. +class BadOptionTypeException : public ConfigurationError { public: - BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {} - BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {} - BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {} + BadOptionTypeException() : ConfigurationError("Bad option type exception") {} + BadOptionTypeException(const std::string &message) : ConfigurationError(message) {} + BadOptionTypeException(const char* message) : ConfigurationError(message) {} +}; + +// Indicate that an option has been deserialized from an invalid value. +class BadOptionValueException : public ConfigurationError +{ +public: + BadOptionValueException() : ConfigurationError("Bad option value exception") {} + BadOptionValueException(const std::string &message) : ConfigurationError(message) {} + BadOptionValueException(const char* message) : ConfigurationError(message) {} }; // Type of a configuration value. @@ -259,7 +274,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; } @@ -267,7 +282,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->value == static_cast*>(&rhs)->value; } @@ -334,7 +349,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->values = static_cast*>(rhs)->values; } @@ -351,12 +366,12 @@ public: if (opt->type() == this->type()) { auto other = static_cast*>(opt); if (other->values.empty()) - throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector"); + throw ConfigurationError("ConfigOptionVector::set(): Assigning from an empty vector"); this->values.emplace_back(other->values.front()); } else if (opt->type() == this->scalar_type()) this->values.emplace_back(static_cast*>(opt)->value); else - throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type"); } } @@ -375,12 +390,12 @@ public: // Assign the first value of the rhs vector. auto other = static_cast*>(rhs); if (other->values.empty()) - throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector"); + throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector"); this->values[i] = other->get_at(j); } else if (rhs->type() == this->scalar_type()) this->values[i] = static_cast*>(rhs)->value; else - throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type"); } const T& get_at(size_t i) const @@ -405,9 +420,9 @@ public: else if (n > this->values.size()) { if (this->values.empty()) { if (opt_default == nullptr) - throw Slic3r::RuntimeError("ConfigOptionVector::resize(): No default value provided."); + throw ConfigurationError("ConfigOptionVector::resize(): No default value provided."); if (opt_default->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type."); + throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type."); this->values.resize(n, static_cast*>(opt_default)->values.front()); } else { // Resize by duplicating the last value. @@ -424,7 +439,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionVector: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->values == static_cast*>(&rhs)->values; } @@ -444,9 +459,9 @@ public: // An option overrides another option if it is not nil and not equal. bool overriden_by(const ConfigOption *rhs) const override { if (this->nullable()) - throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); + throw ConfigurationError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types."); + throw ConfigurationError("ConfigOptionVector.overriden_by() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) // Overridding a non-nullable object with another non-nullable object. @@ -464,9 +479,9 @@ public: // Apply an override option, possibly a nullable one. bool apply_override(const ConfigOption *rhs) override { if (this->nullable()) - throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); + throw ConfigurationError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types."); + throw ConfigurationError("ConfigOptionVector.apply_override() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { // Overridding a non-nullable object with another non-nullable object. @@ -557,7 +572,7 @@ public: bool operator< (const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_lower(this->values, rhs.values); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return vectors_equal(this->values, static_cast*>(&rhs)->values); } @@ -604,7 +619,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); double value; @@ -629,9 +644,9 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else - throw Slic3r::RuntimeError("Serializing invalid number"); + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -763,7 +778,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); int value; @@ -780,7 +795,7 @@ private: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else ss << v; } @@ -970,7 +985,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types"); assert(dynamic_cast(&rhs)); return *this == *static_cast(&rhs); } @@ -986,7 +1001,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); assert(dynamic_cast(rhs)); *this = *static_cast(rhs); } @@ -1030,7 +1045,7 @@ public: bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return vectors_equal(this->values, static_cast*>(&rhs)->values); } @@ -1079,7 +1094,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { bool percent = item_str.find_first_of("%") != std::string::npos; std::istringstream iss(item_str); @@ -1107,9 +1122,9 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else - throw Slic3r::RuntimeError("Serializing invalid number"); + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -1398,7 +1413,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else this->values.push_back(item_str.compare("1") == 0); } @@ -1411,7 +1426,7 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else ss << (v ? "1" : "0"); } @@ -1449,14 +1464,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnum: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionEnum: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == (T)rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnum: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionEnum: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = (T)rhs->getInt(); } @@ -1519,14 +1534,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = rhs->getInt(); } @@ -1599,7 +1614,7 @@ public: case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; } - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1618,7 +1633,7 @@ public: case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); } } } @@ -1630,7 +1645,7 @@ public: case coInts: archive(*static_cast(opt)); break; case coPercents: archive(*static_cast(opt));break; case coBools: archive(*static_cast(opt)); break; - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1649,7 +1664,7 @@ public: case coBool: archive(*static_cast(opt)); break; case coBools: archive(*static_cast(opt)); break; case coEnum: archive(*static_cast(opt)); break; - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); } } // Make the compiler happy, shut up the warnings. @@ -1772,7 +1787,7 @@ public: return out; } - /// Iterate through all of the CLI options and write them to a stream. + // Iterate through all of the CLI options and write them to a stream. std::ostream& print_cli_help( std::ostream& out, bool show_defaults, std::function filter = [](const ConfigOptionDef &){ return true; }) const; @@ -2121,9 +2136,9 @@ private: template void serialize(Archive &ar) { ar(options); } }; -/// Configuration store with a static definition of configuration values. -/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, -/// because the configuration values could be accessed directly. +// Configuration store with a static definition of configuration values. +// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, +// because the configuration values could be accessed directly. class StaticConfig : public virtual ConfigBase { public: diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 53743411c2..adb84df67c 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -723,8 +723,8 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw } catch (const std::ifstream::failure &err) { throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what()); } catch (const boost::property_tree::file_parser_error &err) { - throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%") - % err.filename() % err.message() % err.line()).str()); + throw Slic3r::RuntimeError(format("Failed loading the Config Bundle \"%1%\": %2% at line %3%", + err.filename(), err.message(), err.line())); } catch (const std::runtime_error &err) { throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); } @@ -732,23 +732,27 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw // 2) Continue based on the type of the configuration file. ConfigFileType config_file_type = guess_config_file_type(tree); ConfigSubstitutions config_substitutions; - switch (config_file_type) { - case CONFIG_FILE_TYPE_UNKNOWN: - throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); - case CONFIG_FILE_TYPE_APP_CONFIG: - throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); - case CONFIG_FILE_TYPE_CONFIG: - { - // Initialize a config from full defaults. - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config_substitutions = config.load(tree, compatibility_rule); - Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } - case CONFIG_FILE_TYPE_CONFIG_BUNDLE: - return load_config_file_config_bundle(path, tree, compatibility_rule); + try { + switch (config_file_type) { + case CONFIG_FILE_TYPE_UNKNOWN: + throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); + case CONFIG_FILE_TYPE_APP_CONFIG: + throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); + case CONFIG_FILE_TYPE_CONFIG: + { + // Initialize a config from full defaults. + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + config_substitutions = config.load(tree, compatibility_rule); + Preset::normalize(config); + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } + case CONFIG_FILE_TYPE_CONFIG_BUNDLE: + return load_config_file_config_bundle(path, tree, compatibility_rule); + } + } catch (const ConfigurationError &e) { + throw Slic3r::RuntimeError(format("Invalid configuration file %1%: %2%", path, e.what())); } // This shall never happen. Suppres compiler warnings. @@ -1244,7 +1248,7 @@ std::pair PresetBundle::load_configbundle( active_sla_material = kvp.second.data(); } else if (kvp.first == "printer") { active_printer = kvp.second.data(); - }else if (kvp.first == "physical_printer") { + } else if (kvp.first == "physical_printer") { active_physical_printer = kvp.second.data(); } } @@ -1281,32 +1285,36 @@ std::pair PresetBundle::load_configbundle( DynamicPrintConfig config; std::string alias_name; std::vector renamed_from; - auto parse_config_section = [§ion, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) { - substitution_context.substitutions.clear(); - for (auto &kvp : section.second) { - if (kvp.first == "alias") - alias_name = kvp.second.data(); - else if (kvp.first == "renamed_from") { - if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" << - section.first << "\" contains invalid \"renamed_from\" key, which is being ignored."; - } - } - // Throws on parsing error. For system presets, no substituion is being done, but an exception is thrown. - config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + try { + auto parse_config_section = [§ion, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) { + substitution_context.substitutions.clear(); + for (auto &kvp : section.second) { + if (kvp.first == "alias") + alias_name = kvp.second.data(); + else if (kvp.first == "renamed_from") { + if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) { + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" << + section.first << "\" contains invalid \"renamed_from\" key, which is being ignored."; + } + } + // Throws on parsing error. For system presets, no substituion is being done, but an exception is thrown. + config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + } + }; + if (presets == &this->printers) { + // Select the default config based on the printer_technology field extracted from kvp. + DynamicPrintConfig config_src; + parse_config_section(config_src); + default_config = &presets->default_preset_for(config_src).config; + config = *default_config; + config.apply(config_src); + } else { + default_config = &presets->default_preset().config; + config = *default_config; + parse_config_section(config); } - }; - if (presets == &this->printers) { - // Select the default config based on the printer_technology field extracted from kvp. - DynamicPrintConfig config_src; - parse_config_section(config_src); - default_config = &presets->default_preset_for(config_src).config; - config = *default_config; - config.apply(config_src); - } else { - default_config = &presets->default_preset().config; - config = *default_config; - parse_config_section(config); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what()); } Preset::normalize(config); // Report configuration fields, which are misplaced into a wrong group. @@ -1414,8 +1422,12 @@ std::pair PresetBundle::load_configbundle( DynamicPrintConfig config = default_config; substitution_context.substitutions.clear(); - for (auto& kvp : section.second) - config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + try { + for (auto& kvp : section.second) + config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what()); + } // Report configuration fields, which are misplaced into a wrong group. std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c57fffa6e3..e74a271b42 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2337,6 +2337,10 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (obj->name.empty()) obj->name = fs::path(obj->input_file).filename().string(); } + } catch (const ConfigurationError &e) { + std::string message = GUI::format(_L("Failed loading file \"%1%\" due to an invalid configuration."), filename.string()) + "\n\n" + e.what(); + GUI::show_error(q, message); + continue; } catch (const std::exception &e) { GUI::show_error(q, e.what()); continue; diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index 7fbf31b113..97729ac8e0 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -92,7 +92,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") { } WHEN("A numeric option is set to a non-numeric value.") { THEN("A BadOptionTypeException exception is thown.") { - REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionTypeException); + REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionValueException); } THEN("The value does not change.") { REQUIRE(config.opt("perimeter_speed")->getFloat() == 60.0); From 0094d50d9372f756afc1e6ea18874accb2055281 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Jun 2021 16:21:56 +0200 Subject: [PATCH 105/240] Move show_substitutions_info() call into UI thread --- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index e964793193..fe429e3141 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -140,27 +140,22 @@ void SLAImportJob::process() if (p->path.empty()) return; std::string path = p->path.ToUTF8().data(); - ConfigSubstitutions config_substitutions; try { switch (p->sel) { case Sel::modelAndProfile: - config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr); + p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr); break; case Sel::modelOnly: - config_substitutions = import_sla_archive(path, p->win, p->mesh, progr); + p->config_substitutions = import_sla_archive(path, p->win, p->mesh, progr); break; case Sel::profileOnly: - config_substitutions = import_sla_archive(path, p->profile); + p->config_substitutions = import_sla_archive(path, p->profile); break; } } catch (std::exception &ex) { p->err = ex.what(); } - - if (! config_substitutions.empty()) { - show_substitutions_info(config_substitutions, path); - } update_status(100, was_canceled() ? _(L("Importing canceled.")) : _(L("Importing done."))); @@ -187,6 +182,7 @@ void SLAImportJob::prepare() p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath(); p->sel = dlg.get_selection(); p->win = dlg.get_marchsq_windowsize(); + p->config_substitutions.clear(); } else { p->path = ""; } @@ -230,8 +226,11 @@ void SLAImportJob::finalize() p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh}, name, is_centered); } - + + if (! p->config_substitutions.empty()) + show_substitutions_info(p->config_substitutions, p->path.ToUTF8().data()); + reset(); } -}} +}} // namespace Slic3r::GUI From a25d2d1de49547c1f0c1f939dce6a7f1ac0e2d63 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Jun 2021 17:29:13 +0200 Subject: [PATCH 106/240] Support for SL1S printer: Where SL1 is mentioned, mention it as SL1 / SL1S. Support loading / saving of SL1S files in addition to SL1. --- src/libslic3r/Format/SL1.cpp | 4 ++-- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 6d779b94e3..e2ef801f27 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg) if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || !opt_mirror_x || !opt_mirror_y || !opt_orient) - throw Slic3r::FileIOError("Invalid SL1 file"); + throw Slic3r::FileIOError("Invalid SL1 / SL1S file"); RasterParams rstp; @@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg) auto *opt_init_layerh = cfg.option("initial_layer_height"); if (!opt_layerh || !opt_init_layerh) - throw Slic3r::FileIOError("Invalid SL1 file"); + throw Slic3r::FileIOError("Invalid SL1 / SL1S file"); return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 267ea602fe..5f74a72651 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -234,7 +234,7 @@ void PrintConfigDef::init_common_params() def = this->add("thumbnails", coPoints); def->label = L("G-code thumbnails"); - def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\""); + def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); def->mode = comExpert; def->gui_type = ConfigOptionDef::GUIType::one_string; def->set_default_value(new ConfigOptionPoints()); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b496c846dc..53b07728ea 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -435,7 +435,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG", - /* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1", + /* FT_PNGZIP */ "Masked SLA files (*.sl1, *sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", }; std::string out = defaults[file_type]; diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index fe429e3141..6a22c91309 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -33,7 +33,7 @@ public: m_filepicker = new wxFilePickerCtrl(this, wxID_ANY, from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")), - "SL1 archive files (*.sl1, *.zip)|*.sl1;*.SL1;*.zip;*.ZIP", + "SL1 archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP", wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST); szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 93641adff9..ddf8e6acba 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1188,7 +1188,7 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(true); }, "import_plater", nullptr, [this](){return m_plater != nullptr; }, this); - append_menu_item(import_menu, wxID_ANY, _L("Import SL1 archive") + dots, _L("Load an SL1 archive"), + append_menu_item(import_menu, wxID_ANY, _L("Import SL1 / SL1S archive") + dots, _L("Load an SL1 / Sl1S archive"), [this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr, [this](){return m_plater != nullptr; }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e74a271b42..c73233d6b8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5303,7 +5303,7 @@ void Plater::export_gcode(bool prefer_removable) fs::path output_path; { - wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 file as:"), + wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"), start_dir, from_path(default_output_file.filename()), GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index f01e3ad416..ee669c36fe 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -182,7 +182,7 @@ const char* SL1Host::get_name() const { return "SL1Host"; } wxString SL1Host::get_test_ok_msg () const { - return _(L("Connection to Prusa SL1 works correctly.")); + return _(L("Connection to Prusa SL1 / SL1S works correctly.")); } wxString SL1Host::get_test_failed_msg (wxString &msg) const From cfc0ae7ef3def7740192d344f429b6b543720423 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Jun 2021 17:43:11 +0200 Subject: [PATCH 107/240] Ammended the previous commit (SL1 / SL1S in file picker) --- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 6a22c91309..45b432b5c7 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -33,7 +33,7 @@ public: m_filepicker = new wxFilePickerCtrl(this, wxID_ANY, from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")), - "SL1 archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP", + "SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP", wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST); szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER); From 21dfb0538458d589c9f15713944bfac7c82bdaf7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 30 Jun 2021 14:19:20 +0200 Subject: [PATCH 108/240] InfoDialog: Improvements * Fixed localization * Substitutions are aligned in table --- src/slic3r/GUI/GUI.cpp | 54 +++++++++++++++++++++--------------- src/slic3r/GUI/MsgDialog.cpp | 18 ++++++++++-- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 616d36ba2b..9fd7f6af1d 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -245,25 +245,32 @@ void warning_catcher(wxWindow* parent, const wxString& message) msg.ShowModal(); } +static wxString bold(const wxString& str) +{ + return wxString::Format("\"%s\"", str); +}; + static void add_config_substitutions(const ConfigSubstitutions& conf_substitutions, wxString& changes) { + changes += ""; for (const ConfigSubstitution& conf_substitution : conf_substitutions) { wxString new_val; - if (!conf_substitution.opt_def) + const ConfigOptionDef* def = conf_substitution.opt_def; + if (!def) continue; - if (conf_substitution.opt_def->type == coEnum) { - const std::vector& labels = conf_substitution.opt_def->enum_labels; - const std::vector& values = conf_substitution.opt_def->enum_values; + if (def->type == coEnum) { + const std::vector& labels = def->enum_labels; + const std::vector& values = def->enum_values; int val = conf_substitution.new_value->getInt(); - bool is_infill = conf_substitution.opt_def->opt_key == "top_fill_pattern" || - conf_substitution.opt_def->opt_key == "bottom_fill_pattern" || - conf_substitution.opt_def->opt_key == "fill_pattern"; + bool is_infill = def->opt_key == "top_fill_pattern" || + def->opt_key == "bottom_fill_pattern" || + def->opt_key == "fill_pattern"; // Each infill doesn't use all list of infill declared in PrintConfig.hpp. // So we should "convert" val to the correct one if (is_infill) { - for (const auto& key_val : *conf_substitution.opt_def->enum_keys_map) + for (const auto& key_val : *def->enum_keys_map) if ((int)key_val.second == val) { auto it = std::find(values.begin(), values.end(), key_val.first); if (it == values.end()) @@ -271,16 +278,20 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio new_val = from_u8(_utf8(labels[it - values.begin()])); break; } - new_val = _L("Undef"); + if (new_val.IsEmpty()) + new_val = _L("Undef"); } else new_val = from_u8(_utf8(labels[val])); } - else if (conf_substitution.opt_def->type == coBool) + else if (def->type == coBool) new_val = conf_substitution.new_value->getBool() ? "true" : "false"; - changes += "\n" + GUI::format(_L("New unknown value \"%1%\" was changed to defaul value \"%2%\""), conf_substitution.old_value, new_val); + changes += ""; } + changes += "
" + bold(_(def->label)) + ": " + + format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) + + "
"; } void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions) @@ -288,27 +299,26 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su wxString changes; auto preset_type_name = [](Preset::Type type) { - return type == Slic3r::Preset::TYPE_PRINT ? _L("Print") : - type == Slic3r::Preset::TYPE_SLA_PRINT ? _L("SLA Print") : - type == Slic3r::Preset::TYPE_FILAMENT ? _L("Filament") : - type == Slic3r::Preset::TYPE_SLA_MATERIAL ? _L("SLA Material") : - type == Slic3r::Preset::TYPE_PRINTER ? _L("Printer") : ""; + return type == Slic3r::Preset::TYPE_PRINT ? _L("Print settings") : + type == Slic3r::Preset::TYPE_SLA_PRINT ? _L("SLA print settings") : + type == Slic3r::Preset::TYPE_FILAMENT ? _L("Filament") : + type == Slic3r::Preset::TYPE_SLA_MATERIAL ? _L("SLA material") : + type == Slic3r::Preset::TYPE_PRINTER ? _L("Printer") : "" ; }; for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { - changes += "\n

" + GUI::format(_L(" %1% Preset : %2%"), preset_type_name(substitution.preset_type), substitution.preset_name); + changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold(substitution.preset_name)); if (!substitution.preset_file.empty()) - changes += GUI::format(" (%1%)", substitution.preset_file); - changes += "

"; + changes += format_wxstr(" (%1%)", substitution.preset_file); add_config_substitutions(substitution.substitutions, changes); } if (!changes.IsEmpty()) changes += "\n\n"; - wxString message = format(_L("Loading profiles found following incompatibilities:%1%" - " To recover these files, incompatible values were changed to default values.\n" - " But data in files won't be changed until you save them in PrusaSlicer."), changes); + wxString message = format_wxstr( _L("Loading profiles found following incompatibilities:%1%" + " To recover these files, incompatible values were changed to default values.\n" + " But data in files won't be changed until you save them in PrusaSlicer."), changes); InfoDialog msg(nullptr, message); msg.ShowModal(); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index ccbdf2e150..1e05ab8f15 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -223,7 +223,6 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString& msg) // Text shown as HTML, so that mouse selection and Ctrl-V to copy will work. wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); { - html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont monospace = wxGetApp().code_font(); wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); @@ -234,6 +233,22 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString& msg) int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size }; html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size); html->SetBorders(2); + + // calculate html page size from text + int lines = msg.Freq('\n'); + + if (msg.Contains("")) { + int pos = 0; + while (pos < (int)msg.Len() && pos != wxNOT_FOUND) { + pos = msg.find("", pos + 1); + lines+=2; + } + } + int page_height = std::min((font.GetPixelSize().y + 1) * lines, 68 * wxGetApp().em_unit()); + wxSize page_size(68 * wxGetApp().em_unit(), page_height); + + html->SetMinSize(page_size); + std::string msg_escaped = xml_escape(msg.ToUTF8().data(), true); boost::replace_all(msg_escaped, "\r\n", "
"); boost::replace_all(msg_escaped, "\n", "
"); @@ -244,7 +259,6 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString& msg) // Set info bitmap logo->SetBitmap(create_scaled_bitmap("info", this, 84)); - SetMinSize(wxSize(60 * wxGetApp().em_unit(), 30 * wxGetApp().em_unit())); Fit(); } From ca530717e0d2a9d6bed174d31b45b4277d51df3f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 28 Jun 2021 19:30:31 +0200 Subject: [PATCH 109/240] MSW specific: Fixed a crash on change of the extruder using keyboard --- src/slic3r/GUI/ExtraRenderers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 277f225afb..e9fb7339f3 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -315,9 +315,11 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR // to avoid event propagation to other sidebar items c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { evt.StopPropagation(); +#ifdef __linux__ // FinishEditing grabs new selection and triggers config update. We better call // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. this->FinishEditing(); +#endif }); return c_editor; From e199dd5b2002fa8866d491442acc634f7fcca968 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 30 Jun 2021 14:32:39 +0200 Subject: [PATCH 110/240] Follow-up https://github.com/prusa3d/PrusaSlicer/commit/88d5f40b7c7c0c8ca19ac60dfdb12719a32e1917 - Small fix for text formatting --- src/slic3r/GUI/GUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 9fd7f6af1d..db092c85ce 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -333,9 +333,9 @@ void show_substitutions_info(const ConfigSubstitutions& config_substitutions, co if (!changes.IsEmpty()) changes += "\n\n"; - wxString message = format(_L("Loading %1% file found incompatibilities.\n" + wxString message = format(_L("Loading %1% file found incompatibilities.\n" "To recover this file, incompatible values were changed to default values:%2%" - "But data in files won't be changed until you save them in PrusaSlicer."), from_u8(filename), changes); + "But data in files won't be changed until you save them in PrusaSlicer."), bold(from_u8(filename)), changes); InfoDialog msg(nullptr, message); msg.ShowModal(); From 4fa651456dc150ee569bd82d9c03ed0c29f0e6c2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Jun 2021 15:19:47 +0200 Subject: [PATCH 111/240] Support for CurWa CW1S firmware updater. --- src/slic3r/GUI/FirmwareDialog.cpp | 13 +++++++++---- src/slic3r/Utils/HexFile.cpp | 1 + src/slic3r/Utils/HexFile.hpp | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index c46dc49bcf..4966726ae8 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -65,6 +65,8 @@ enum { USB_PID_MMU_APP = 4, USB_PID_CW1_BOOT = 7, USB_PID_CW1_APP = 8, + USB_PID_CW1S_BOOT = 14, + USB_PID_CW1S_APP = 15, }; // This enum discriminates the kind of information in EVT_AVRDUDE, @@ -308,7 +310,7 @@ void FirmwareDialog::priv::update_flash_enabled() void FirmwareDialog::priv::load_hex_file(const wxString &path) { hex_file = HexFile(path.wx_str()); - const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1; + const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1 || hex_file.device == HexFile::DEV_CW1S; set_autodetect(autodetect); } @@ -636,6 +638,10 @@ void FirmwareDialog::priv::perform_upload() this->prepare_avr109(Avr109Pid(USB_PID_CW1_BOOT, USB_PID_CW1_APP)); break; + case HexFile::DEV_CW1S: + this->prepare_avr109(Avr109Pid(USB_PID_CW1S_BOOT, USB_PID_CW1S_APP)); + break; + default: this->prepare_mk2(); break; @@ -767,11 +773,10 @@ const char* FirmwareDialog::priv::avr109_dev_name(Avr109Pid usb_pid) { switch (usb_pid.boot) { case USB_PID_MMU_BOOT: return "Original Prusa MMU 2.0 Control"; - break; case USB_PID_CW1_BOOT: return "Original Prusa CW1"; - break; - + case USB_PID_CW1S_BOOT: + return "Original Prusa CW1S"; default: throw Slic3r::RuntimeError((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str()); } } diff --git a/src/slic3r/Utils/HexFile.cpp b/src/slic3r/Utils/HexFile.cpp index 26596f629e..a13fcab02b 100644 --- a/src/slic3r/Utils/HexFile.cpp +++ b/src/slic3r/Utils/HexFile.cpp @@ -19,6 +19,7 @@ static HexFile::DeviceKind parse_device_kind(const std::string &str) else if (str == "mk3") { return HexFile::DEV_MK3; } else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; } else if (str == "cw1") { return HexFile::DEV_CW1; } + else if (str == "cw1s") { return HexFile::DEV_CW1S; } else { return HexFile::DEV_GENERIC; } } diff --git a/src/slic3r/Utils/HexFile.hpp b/src/slic3r/Utils/HexFile.hpp index 742ae00e6a..b32d110edc 100644 --- a/src/slic3r/Utils/HexFile.hpp +++ b/src/slic3r/Utils/HexFile.hpp @@ -17,6 +17,7 @@ struct HexFile DEV_MK3, DEV_MM_CONTROL, DEV_CW1, + DEV_CW1S, }; boost::filesystem::path path; From c7691ec95e52ce72c5af96a52c21ee575b0209fb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Jun 2021 16:19:06 +0200 Subject: [PATCH 112/240] Forward compatibility - config substitutions: 1) Verify whether a value looks like an enum 2) Always report substitution of an enum with a boolean. --- src/libslic3r/Config.cpp | 15 ++++++++++++++- src/libslic3r/Config.hpp | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index e0c639fdf0..f93d98e1ba 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -506,6 +506,16 @@ void ConfigBase::set_deserialize(std::initializer_list items this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append); } +static inline bool looks_like_enum_value(const std::string &s) +{ + if (value.empty() || value.size() > 64 || ! isalpha(value.front())) + return false; + for (const char c : s) + if (! (isalnum(c) || c == '_' || c == '-')) + return false; + return true; +} + bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append) { t_config_option_key opt_key = opt_key_src; @@ -551,7 +561,10 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con // Deserialize failed, try to substitute with a default value. assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); - opt->set(optdef->default_value.get()); + if (optdef->type == coBool && looks_like_enum_value(value)) + static_cast(opt)->value = boost::iequals(value, "enabled") || boost::iequals(value, "on"); + else + opt->set(optdef->default_value.get()); if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) { diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 078e94cfb0..088eec33ab 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1330,11 +1330,11 @@ public: bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - if (str == "1" || boost::iequals(str, "enabled") || boost::iequals(str, "on")) { + if (str == "1") { this->value = true; return true; } - if (str == "0" || boost::iequals(str, "disabled") || boost::iequals(str, "off")) { + if (str == "0") { this->value = false; return true; } From bd159f4e524bcb012e1b435df5c7cad16e8f60f8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Jun 2021 16:26:52 +0200 Subject: [PATCH 113/240] Fixed typo in SL1S file mask template. --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 53b07728ea..bfead94727 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -435,7 +435,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG", - /* FT_PNGZIP */ "Masked SLA files (*.sl1, *sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", + /* FT_PNGZIP */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", }; std::string out = defaults[file_type]; From 5b843aa291572b121e53aaaa622388367e528acb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Jun 2021 16:52:44 +0200 Subject: [PATCH 114/240] Fix of the previous commit. --- src/libslic3r/Config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index f93d98e1ba..22afea92f8 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -506,11 +506,11 @@ void ConfigBase::set_deserialize(std::initializer_list items this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append); } -static inline bool looks_like_enum_value(const std::string &s) +static inline bool looks_like_enum_value(const std::string &value) { if (value.empty() || value.size() > 64 || ! isalpha(value.front())) return false; - for (const char c : s) + for (const char c : value) if (! (isalnum(c) || c == '_' || c == '-')) return false; return true; From 3a0b71deede656bdcccb6279a3453493d73ea3c4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Jun 2021 18:55:43 +0200 Subject: [PATCH 115/240] Configuration compatibility - implemented substitution and reporting for vectors of bools (including the nullable bools). --- src/libslic3r/Config.cpp | 67 ++++++++++++++++++++-------------------- src/libslic3r/Config.hpp | 47 +++++++++++++++++++++++++--- src/slic3r/GUI/GUI.cpp | 21 +++++++++++-- 3 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 22afea92f8..dbe038c5bf 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -506,16 +506,6 @@ void ConfigBase::set_deserialize(std::initializer_list items this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append); } -static inline bool looks_like_enum_value(const std::string &value) -{ - if (value.empty() || value.size() > 64 || ! isalpha(value.front())) - return false; - for (const char c : value) - if (! (isalnum(c) || c == '_' || c == '-')) - return false; - return true; -} - bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append) { t_config_option_key opt_key = opt_key_src; @@ -552,30 +542,41 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con ConfigOption *opt = this->option(opt_key, true); assert(opt != nullptr); - bool success = opt->deserialize(value, append); - if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable && - // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value. - // That means, we expect enum values being added in the future and possibly booleans being converted to enums. - (optdef->type == coEnum || optdef->type == coBool)) - { - // Deserialize failed, try to substitute with a default value. - assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); + bool success = false; + bool substituted = false; + if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) { + //FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release. + auto result = opt->nullable() ? + static_cast(opt)->deserialize_with_substitutions(value, append, true) : + static_cast(opt)->deserialize_with_substitutions(value, append, true); + success = result != ConfigHelpers::DeserializationResult::Failed; + substituted = result == ConfigHelpers::DeserializationResult::Substituted; + } else { + success = opt->deserialize(value, append); + if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable && + // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value. + // That means, we expect enum values being added in the future and possibly booleans being converted to enums. + (optdef->type == coEnum || optdef->type == coBool) && ConfigHelpers::looks_like_enum_value(value)) { + // Deserialize failed, try to substitute with a default value. + assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); + if (optdef->type == coBool) + static_cast(opt)->value = ConfigHelpers::enum_looks_like_true_value(value); + else + // Just use the default of the option. + opt->set(optdef->default_value.get()); + success = true; + substituted = true; + } + } - if (optdef->type == coBool && looks_like_enum_value(value)) - static_cast(opt)->value = boost::iequals(value, "enabled") || boost::iequals(value, "on"); - else - opt->set(optdef->default_value.get()); - - if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || - substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) { - // Log the substitution. - ConfigSubstitution config_substitution; - config_substitution.opt_def = optdef; - config_substitution.old_value = value;//std::unique_ptr(opt); - config_substitution.new_value = ConfigOptionUniquePtr(this->option(opt_key, true)->clone()); - substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution)); - } - return true; + if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || + substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) { + // Log the substitution. + ConfigSubstitution config_substitution; + config_substitution.opt_def = optdef; + config_substitution.old_value = value; + config_substitution.new_value = ConfigOptionUniquePtr(opt->clone()); + substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution)); } return success; } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 088eec33ab..7d66264f5d 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -80,6 +80,30 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< extern std::string escape_ampersand(const std::string& str); +namespace ConfigHelpers { + inline bool looks_like_enum_value(std::string value) + { + boost::trim(value); + if (value.empty() || value.size() > 64 || ! isalpha(value.front())) + return false; + for (const char c : value) + if (! (isalnum(c) || c == '_' || c == '-')) + return false; + return true; + } + + inline bool enum_looks_like_true_value(std::string value) { + boost::trim(value); + return boost::iequals(value, "enabled") || boost::iequals(value, "on"); + } + + enum DeserializationResult { + Loaded, + Substituted, + Failed, + }; +}; + // Base for all exceptions thrown by the configuration layer. class ConfigurationError : public Slic3r::RuntimeError { public: @@ -1400,24 +1424,39 @@ public: } return vv; } - - bool deserialize(const std::string &str, bool append = false) override + + ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, bool substitute) { if (! append) this->values.clear(); std::istringstream is(str); std::string item_str; + bool substituted = false; while (std::getline(is, item_str, ',')) { boost::trim(item_str); + unsigned char new_value = 0; if (item_str == "nil") { if (NULLABLE) this->values.push_back(nil_value()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); + } else if (item_str == "1") { + new_value = true; + } else if (item_str == "0") { + new_value = false; + } else if (substitute && ConfigHelpers::looks_like_enum_value(item_str)) { + new_value = ConfigHelpers::enum_looks_like_true_value(item_str); + substituted = true; } else - this->values.push_back(item_str.compare("1") == 0); + return ConfigHelpers::DeserializationResult::Failed; + this->values.push_back(new_value); } - return true; + return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded; + } + + bool deserialize(const std::string &str, bool append = false) override + { + return this->deserialize_with_substitutions(str, append, false) == ConfigHelpers::DeserializationResult::Loaded; } protected: diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index db092c85ce..5b3058fd67 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -258,7 +258,9 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio const ConfigOptionDef* def = conf_substitution.opt_def; if (!def) continue; - if (def->type == coEnum) { + switch (def->type) { + case coEnum: + { const std::vector& labels = def->enum_labels; const std::vector& values = def->enum_values; int val = conf_substitution.new_value->getInt(); @@ -283,9 +285,24 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio } else new_val = from_u8(_utf8(labels[val])); + break; } - else if (def->type == coBool) + case coBool: new_val = conf_substitution.new_value->getBool() ? "true" : "false"; + break; + case coBools: + if (conf_substitution.new_value->nullable()) + for (const char v : static_cast(conf_substitution.new_value.get())->values) + new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", "; + else + for (const char v : static_cast(conf_substitution.new_value.get())->values) + new_val += std::string(v ? "true" : "false") + ", "; + if (! new_val.empty()) + new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end()); + break; + default: + assert(false); + } changes += "" + bold(_(def->label)) + ": " + format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) + From 26822347ed42f5d0d650d78f2608f472525e9796 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Jul 2021 08:44:02 +0200 Subject: [PATCH 116/240] Forward compatibility, parameter susbtitution: Substitute vector values (extruder specific) with their default, if the default is a single value vector. Show the "Physical Printers" label in the substitution window. --- src/libslic3r/Config.cpp | 15 ++++++++++++--- src/libslic3r/Config.hpp | 14 ++++++++++---- src/slic3r/GUI/GUI.cpp | 14 +++++++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index dbe038c5bf..46cbd9891c 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -546,9 +546,18 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con bool substituted = false; if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) { //FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release. - auto result = opt->nullable() ? - static_cast(opt)->deserialize_with_substitutions(value, append, true) : - static_cast(opt)->deserialize_with_substitutions(value, append, true); + bool nullable = opt->nullable(); + ConfigHelpers::DeserializationSubstitution default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToFalse; + if (optdef->default_value) { + // Default value for vectors of booleans used in a "per extruder" context, thus the default contains just a single value. + assert(dynamic_cast*>(optdef->default_value.get())); + auto &values = static_cast*>(optdef->default_value.get())->values; + if (values.size() == 1 && values.front() == 1) + default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToTrue; + } + auto result = nullable ? + static_cast(opt)->deserialize_with_substitutions(value, append, default_value) : + static_cast(opt)->deserialize_with_substitutions(value, append, default_value); success = result != ConfigHelpers::DeserializationResult::Failed; substituted = result == ConfigHelpers::DeserializationResult::Substituted; } else { diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 7d66264f5d..a3d86d1453 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -97,6 +97,12 @@ namespace ConfigHelpers { return boost::iequals(value, "enabled") || boost::iequals(value, "on"); } + enum DeserializationSubstitution { + Disabled, + DefaultsToFalse, + DefaultsToTrue + }; + enum DeserializationResult { Loaded, Substituted, @@ -1425,7 +1431,7 @@ public: return vv; } - ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, bool substitute) + ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, ConfigHelpers::DeserializationSubstitution substitution) { if (! append) this->values.clear(); @@ -1444,8 +1450,8 @@ public: new_value = true; } else if (item_str == "0") { new_value = false; - } else if (substitute && ConfigHelpers::looks_like_enum_value(item_str)) { - new_value = ConfigHelpers::enum_looks_like_true_value(item_str); + } else if (substitution != ConfigHelpers::DeserializationSubstitution::Disabled && ConfigHelpers::looks_like_enum_value(item_str)) { + new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substituted == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue; substituted = true; } else return ConfigHelpers::DeserializationResult::Failed; @@ -1456,7 +1462,7 @@ public: bool deserialize(const std::string &str, bool append = false) override { - return this->deserialize_with_substitutions(str, append, false) == ConfigHelpers::DeserializationResult::Loaded; + return this->deserialize_with_substitutions(str, append, ConfigHelpers::DeserializationSubstitution::Disabled) == ConfigHelpers::DeserializationResult::Loaded; } protected: diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 5b3058fd67..17a46d3695 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -316,11 +316,15 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su wxString changes; auto preset_type_name = [](Preset::Type type) { - return type == Slic3r::Preset::TYPE_PRINT ? _L("Print settings") : - type == Slic3r::Preset::TYPE_SLA_PRINT ? _L("SLA print settings") : - type == Slic3r::Preset::TYPE_FILAMENT ? _L("Filament") : - type == Slic3r::Preset::TYPE_SLA_MATERIAL ? _L("SLA material") : - type == Slic3r::Preset::TYPE_PRINTER ? _L("Printer") : "" ; + switch (type) { + case Preset::TYPE_PRINT: return _L("Print settings"); + case Preset::TYPE_SLA_PRINT: return _L("SLA print settings"); + case Preset::TYPE_FILAMENT: return _L("Filament"); + case Preset::TYPE_SLA_MATERIAL: return _L("SLA material"); + case Preset::TYPE_PRINTER: return _L("Printer"); + case Preset::TYPE_PHYSICAL_PRINTER: return _L("Physical Printer"); + default: assert(false); return wxString(); + } }; for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { From 14330b02b36d8939fea915ed3e4508da8478e9b0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Jul 2021 16:25:17 +0200 Subject: [PATCH 117/240] Workaround for OSX file picker. Saving SL1S files did not work, the file picker replaced the SL1S extension with SL1. --- src/slic3r/GUI/GUI_App.cpp | 4 +++- src/slic3r/GUI/GUI_App.hpp | 4 +++- src/slic3r/GUI/Plater.cpp | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bfead94727..3fd823852b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -435,7 +435,9 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG", - /* FT_PNGZIP */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", + /* FT_SL1 */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S", + // Workaround for OSX file picker, for some reason it always saves with the 1st extension. + /* FT_SL1S */ "Masked SLA files (*.sl1s, *.sl1)|*.sl1s;*.SL1S;*.sl1;*.SL1", }; std::string out = defaults[file_type]; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index f23f421332..f6699e82b4 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -66,7 +66,9 @@ enum FileType FT_TEX, - FT_PNGZIP, + FT_SL1, + // Workaround for OSX file picker, for some reason it always saves with the 1st extension. + FT_SL1S, FT_SIZE, }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c73233d6b8..d3149a2571 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5303,10 +5303,11 @@ void Plater::export_gcode(bool prefer_removable) fs::path output_path; { + std::string ext = default_output_file.extension().string(); wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"), start_dir, from_path(default_output_file.filename()), - GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), + GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (dlg.ShowModal() == wxID_OK) From 16104587ac097630a9ee79c59ecedb4a9fbab2f9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Jul 2021 16:48:38 +0200 Subject: [PATCH 118/240] Improvement in the subtitution information dialog. --- src/libslic3r/Config.hpp | 6 ++-- src/slic3r/GUI/GUI.cpp | 53 ++++++++++++++++++++---------------- src/slic3r/GUI/MsgDialog.cpp | 4 +-- src/slic3r/GUI/Plater.cpp | 2 +- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index a3d86d1453..84f1a8c000 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -97,13 +97,13 @@ namespace ConfigHelpers { return boost::iequals(value, "enabled") || boost::iequals(value, "on"); } - enum DeserializationSubstitution { + enum class DeserializationSubstitution { Disabled, DefaultsToFalse, DefaultsToTrue }; - enum DeserializationResult { + enum class DeserializationResult { Loaded, Substituted, Failed, @@ -1451,7 +1451,7 @@ public: } else if (item_str == "0") { new_value = false; } else if (substitution != ConfigHelpers::DeserializationSubstitution::Disabled && ConfigHelpers::looks_like_enum_value(item_str)) { - new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substituted == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue; + new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substitution == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue; substituted = true; } else return ConfigHelpers::DeserializationResult::Failed; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 17a46d3695..cfc5c13617 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #if __APPLE__ @@ -245,7 +246,12 @@ void warning_catcher(wxWindow* parent, const wxString& message) msg.ShowModal(); } -static wxString bold(const wxString& str) +static wxString bold(const wxString& str) +{ + return wxString::Format("%s", str); +}; + +static wxString bold_string(const wxString& str) { return wxString::Format("\"%s\"", str); }; @@ -277,14 +283,17 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio auto it = std::find(values.begin(), values.end(), key_val.first); if (it == values.end()) break; - new_val = from_u8(_utf8(labels[it - values.begin()])); + auto idx = it - values.begin(); + new_val = wxString("\"") + values[idx] + "\"" + " (" + from_u8(_utf8(labels[idx])) + ")"; break; } - if (new_val.IsEmpty()) - new_val = _L("Undef"); + if (new_val.IsEmpty()) { + assert(false); + new_val = _L("Undefined"); + } } else - new_val = from_u8(_utf8(labels[val])); + new_val = wxString("\"") + values[val] + "\"" + " (" + from_u8(_utf8(labels[val])) + ")"; break; } case coBool: @@ -304,13 +313,21 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio assert(false); } - changes += "" + bold(_(def->label)) + ": " + - format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) + + changes += format_wxstr("\"%1%\" (%2%): ", def->opt_key, _(def->label)) + + format_wxstr(_L("%1% was substituted with %2%"), bold_string(conf_substitution.old_value), bold(new_val)) + ""; } changes += ""; } +static wxString substitution_message(const wxString& changes) +{ + return + _L("Most likely the configuration was produced by a newer version of PrusaSlicer or by some PrusaSlicer fork.") + " " + + _L("The following values were substituted:") + "\n" + changes + "\n\n" + + _L("Please review the substitutions and adjust them if needed."); +} + void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions) { wxString changes; @@ -328,37 +345,25 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su }; for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { - changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold(substitution.preset_name)); + changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(substitution.preset_name)); if (!substitution.preset_file.empty()) changes += format_wxstr(" (%1%)", substitution.preset_file); add_config_substitutions(substitution.substitutions, changes); } - if (!changes.IsEmpty()) - changes += "\n\n"; - wxString message = format_wxstr( _L("Loading profiles found following incompatibilities:%1%" - " To recover these files, incompatible values were changed to default values.\n" - " But data in files won't be changed until you save them in PrusaSlicer."), changes); - - InfoDialog msg(nullptr, message); + InfoDialog msg(nullptr, _L("Configuration bundle was loaded, however some configuration values were not recognized."), substitution_message(changes)); msg.ShowModal(); } void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename) { wxString changes = "\n"; - add_config_substitutions(config_substitutions, changes); - if (!changes.IsEmpty()) - changes += "\n\n"; - - wxString message = format(_L("Loading %1% file found incompatibilities.\n" - "To recover this file, incompatible values were changed to default values:%2%" - "But data in files won't be changed until you save them in PrusaSlicer."), bold(from_u8(filename)), changes); - - InfoDialog msg(nullptr, message); + InfoDialog msg(nullptr, + format_wxstr(_L("Configuration file \"%1%\" was loaded, however some configuration values were not recognized."), from_u8(filename)), + substitution_message(changes)); msg.ShowModal(); } diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 1e05ab8f15..41846500de 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -214,8 +214,8 @@ MessageDialog::MessageDialog(wxWindow* parent, // InfoDialog -InfoDialog::InfoDialog(wxWindow* parent, const wxString& msg) - : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), _L("Note that")) +InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg) + : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title) , msg(msg) { this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d3149a2571..22a1b36ae0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5310,7 +5310,7 @@ void Plater::export_gcode(bool prefer_removable) GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); - if (dlg.ShowModal() == wxID_OK) + if (dlg.ShowModal() == wxID_OK) output_path = into_path(dlg.GetPath()); } From 5ca4848980d002b32b2492afa0abc09acf2d9434 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 2 Jul 2021 08:57:36 +0200 Subject: [PATCH 119/240] Adjusted wording of the config substitution dialog. --- 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 cfc5c13617..59ccd9d66d 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -325,7 +325,7 @@ static wxString substitution_message(const wxString& changes) return _L("Most likely the configuration was produced by a newer version of PrusaSlicer or by some PrusaSlicer fork.") + " " + _L("The following values were substituted:") + "\n" + changes + "\n\n" + - _L("Please review the substitutions and adjust them if needed."); + _L("Review the substitutions and adjust them if needed."); } void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions) From 1c078b1f474ff814b3416919197ba215b3a0e114 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 7 Jul 2021 15:17:21 +0200 Subject: [PATCH 120/240] Improved error handling when installing configuration updates: In case the configuration snapshot cannot be taken before installing configuration updates (because the current configuration state is invalid), ask user whether to continue or abort. --- src/slic3r/Config/Snapshot.cpp | 61 ++++++++++++++++++++++--- src/slic3r/Config/Snapshot.hpp | 7 +++ src/slic3r/GUI/ConfigWizard.cpp | 15 +++--- src/slic3r/GUI/ConfigWizard_private.hpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 13 ++++-- src/slic3r/Utils/PresetUpdater.cpp | 42 +++++++++-------- src/slic3r/Utils/PresetUpdater.hpp | 2 +- 7 files changed, 103 insertions(+), 39 deletions(-) diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 40aade783d..23713dd119 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "libslic3r/PresetBundle.hpp" #include "libslic3r/format.hpp" @@ -17,6 +18,13 @@ #include "libslic3r/FileParserError.hpp" #include "libslic3r/Utils.hpp" +#include "../GUI/GUI.hpp" +#include "../GUI/GUI_App.hpp" +#include "../GUI/I18N.hpp" +#include "../GUI/MainFrame.hpp" + +#include + #define SLIC3R_SNAPSHOTS_DIR "snapshots" #define SLIC3R_SNAPSHOT_FILE "snapshot.ini" @@ -435,14 +443,27 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: } boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; - boost::filesystem::create_directory(snapshot_dir); - // Backup the presets. - for (const char *subdir : snapshot_subdirs) - copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); - snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); - assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); - m_snapshots.emplace_back(std::move(snapshot)); + try { + boost::filesystem::create_directory(snapshot_dir); + + // Backup the presets. + for (const char *subdir : snapshot_subdirs) + copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); + snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); + assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); + m_snapshots.emplace_back(std::move(snapshot)); + } catch (...) { + if (boost::filesystem::is_directory(snapshot_dir)) { + try { + // Clean up partially copied snapshot. + boost::filesystem::remove_all(snapshot_dir); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << "Failed taking snapshot and failed removing the snapshot directory " << snapshot_dir; + } + } + throw; + } return m_snapshots.back(); } @@ -553,6 +574,32 @@ SnapshotDB& SnapshotDB::singleton() return instance; } +const Snapshot* take_config_snapshot_report_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment) +{ + try { + return &SnapshotDB::singleton().take_snapshot(app_config, reason, comment); + } catch (std::exception &err) { + show_error(static_cast(wxGetApp().mainframe), + _L("Taking a configuration snapshot failed.") + "\n\n" + from_u8(err.what())); + return nullptr; + } +} + +bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment, const std::string &message) +{ + try { + SnapshotDB::singleton().take_snapshot(app_config, reason, comment); + return true; + } catch (std::exception &err) { + wxRichMessageDialog dlg(static_cast(wxGetApp().mainframe), + _L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message), + _L("PrusaSlicer error"), + wxYES_NO); + dlg.SetYesNoLabels(_L("Continue"), _L("Abort")); + return dlg.ShowModal() == wxID_YES; + } +} + } // namespace Config } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/Config/Snapshot.hpp b/src/slic3r/Config/Snapshot.hpp index 48add8a1ad..f453006338 100644 --- a/src/slic3r/Config/Snapshot.hpp +++ b/src/slic3r/Config/Snapshot.hpp @@ -127,6 +127,13 @@ private: std::vector m_snapshots; }; +// Take snapshot on SnapshotDB::singleton(). If taking snapshot fails, report an error and return nullptr. +const Snapshot* take_config_snapshot_report_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment); + +// Take snapshot on SnapshotDB::singleton(). If taking snapshot fails, report "message", and present a "Continue" or "Abort" buttons to respond. +// Return true on success and on "Continue" to continue with the process (for example installation of presets). +bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment, const std::string &message); + } // namespace Config } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index e873d9b07c..c31bc02873 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2453,7 +2453,7 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo return true; } -void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) +bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { const auto enabled_vendors = appconfig_new.vendors(); @@ -2508,14 +2508,14 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese break; } - if (snapshot) { - SnapshotDB::singleton().take_snapshot(*app_config, snapshot_reason); - } + if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?"))) + return false; if (install_bundles.size() > 0) { // Install bundles from resources. // Don't create snapshot - we've already done that above if applicable. - updater->install_bundles_rsrc(std::move(install_bundles), false); + if (! updater->install_bundles_rsrc(std::move(install_bundles), false)) + return false; } else { BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; } @@ -2607,6 +2607,8 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // Update the selections from the compatibilty. preset_bundle->export_selections(*app_config); + + return true; } void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add) { @@ -2817,7 +2819,8 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page) p->set_start_page(start_page); if (ShowModal() == wxID_OK) { - p->apply_config(app.app_config, app.preset_bundle, app.preset_updater); + if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater)) + return false; app.app_config->set_legacy_datadir(false); app.update_mode(); app.obj_manipul()->update_ui_from_settings(); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 6ca0619410..84def42025 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -611,7 +611,7 @@ struct ConfigWizard::priv bool on_bnt_finish(); bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string()); - void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); + bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); // #ys_FIXME_alise void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); #ifdef __linux__ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3fd823852b..3973434375 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1862,9 +1862,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) child->SetFont(normal_font()); if (dlg.ShowModal() == wxID_OK) - app_config->set("on_snapshot", - Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot( - *app_config, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id); + if (const Config::Snapshot *snapshot = Config::take_config_snapshot_report_error( + *app_config, Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()); + snapshot != nullptr) + app_config->set("on_snapshot", snapshot->id); } break; case ConfigMenuSnapshots: @@ -1875,8 +1876,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot); dlg.ShowModal(); if (!dlg.snapshot_to_activate().empty()) { - if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config)) - Config::SnapshotDB::singleton().take_snapshot(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); + if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config) && + ! Config::take_config_snapshot_cancel_on_error(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK, "", + GUI::format(_L("Continue to activate a configuration snapshot %1%?", ex.what())))) + break; try { app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); // Enable substitutions, log both user and system substitutions. There should not be any substitutions performed when loading system diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 7b4321be99..29d474dcaf 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -171,7 +171,7 @@ struct PresetUpdater::priv void check_install_indices() const; Updates get_config_updates(const Semver& old_slic3r_version) const; - void perform_updates(Updates &&updates, bool snapshot = true) const; + bool perform_updates(Updates &&updates, bool snapshot = true) const; void set_waiting_updates(Updates u); }; @@ -584,12 +584,14 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version return updates; } -void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const +bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const { if (updates.incompats.size() > 0) { if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; - SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE); + if (! GUI::Config::take_config_snapshot_cancel_on_error(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE, "", + _u8L("Continue and install configuration updates?"))) + return false; } BOOST_LOG_TRIVIAL(info) << format("Deleting %1% incompatible bundles", updates.incompats.size()); @@ -604,7 +606,9 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; - SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); + if (! GUI::Config::take_config_snapshot_cancel_on_error(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE, "", + _u8L("Continue and install configuration updates?"))) + return false; } BOOST_LOG_TRIVIAL(info) << format("Performing %1% updates", updates.updates.size()); @@ -648,6 +652,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); } } } + + return true; } void PresetUpdater::priv::set_waiting_updates(Updates u) @@ -761,11 +767,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 // This effectively removes the incompatible bundles: // (snapshot is taken beforehand) - p->perform_updates(std::move(updates)); - - if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { + if (! p->perform_updates(std::move(updates)) || + ! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) return R_INCOMPAT_EXIT; - } return R_INCOMPAT_CONFIGURED; } @@ -799,7 +803,8 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(info) << "User wants to update..."; - p->perform_updates(std::move(updates)); + if (! p->perform_updates(std::move(updates))) + return R_INCOMPAT_EXIT; reload_configs_update_gui(); return R_UPDATE_INSTALLED; } @@ -828,7 +833,8 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; - p->perform_updates(std::move(updates)); + if (! p->perform_updates(std::move(updates))) + return R_ALL_CANCELED; reload_configs_update_gui(); return R_UPDATE_INSTALLED; } @@ -848,7 +854,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 return R_NOOP; } -void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool snapshot) const +bool PresetUpdater::install_bundles_rsrc(std::vector bundles, bool snapshot) const { Updates updates; @@ -860,7 +866,7 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); } - p->perform_updates(std::move(updates), snapshot); + return p->perform_updates(std::move(updates), snapshot); } void PresetUpdater::on_update_notification_confirm() @@ -880,16 +886,14 @@ void PresetUpdater::on_update_notification_confirm() const auto res = dlg.ShowModal(); if (res == wxID_OK) { BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update"; - p->perform_updates(std::move(p->waiting_updates)); - reload_configs_update_gui(); - p->has_waiting_updates = false; - //return R_UPDATE_INSTALLED; + if (p->perform_updates(std::move(p->waiting_updates))) { + reload_configs_update_gui(); + p->has_waiting_updates = false; + } } else { BOOST_LOG_TRIVIAL(info) << "User refused the update"; - //return R_UPDATE_REJECT; - } - + } } } diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index acec7baf7c..d7eeb5604b 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -52,7 +52,7 @@ public: UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params) const; // "Update" a list of bundles from resources (behaves like an online update). - void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; + bool install_bundles_rsrc(std::vector bundles, bool snapshot = true) const; void on_update_notification_confirm(); private: From b8531b86e4ce41f4a4f0335f8fe41098fa8adbbe Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 7 Jul 2021 15:22:42 +0200 Subject: [PATCH 121/240] Changed one line in CZ dictionary for the Configuration Substitutions dialog. Fixed one bug in the preceding commit. --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3973434375..ec1dce508f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1878,7 +1878,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) if (!dlg.snapshot_to_activate().empty()) { if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config) && ! Config::take_config_snapshot_cancel_on_error(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK, "", - GUI::format(_L("Continue to activate a configuration snapshot %1%?", ex.what())))) + GUI::format(_L("Continue to activate a configuration snapshot %1%?"), dlg.snapshot_to_activate()))) break; try { app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id); From add67d769a7594959357e81bc31f573e6380d669 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 14 Jul 2021 10:13:52 +0200 Subject: [PATCH 122/240] Fix of 2.3.2 regression in configuration layer (reading of nullable boolean vectors) Fix of Filament overrides on saved 3MF MMU profiles are broken #6711 --- src/libslic3r/Config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 84f1a8c000..750b9411ce 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1443,7 +1443,7 @@ public: unsigned char new_value = 0; if (item_str == "nil") { if (NULLABLE) - this->values.push_back(nil_value()); + new_value = nil_value(); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else if (item_str == "1") { From d569789285b9b03fe219e80cb9f527048266e67b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 13 Aug 2021 14:53:13 +0200 Subject: [PATCH 123/240] Fixes after merging 2.3.2/2.3.3 changes from stable to master. --- src/libslic3r/Config.cpp | 33 ---------------------------- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 1 + src/slic3r/GUI/MsgDialog.hpp | 16 ++++++++++++++ 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 46cbd9891c..1fe4816ad3 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -728,39 +728,6 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo return std::move(substitutions_ctxt.substitutions); } -// Load the config keys from the tail of a G-code file. -ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) -{ - try { - // Read a 64k block from the end of the G-code. - boost::nowide::ifstream ifs(file); - { - const char slic3r_gcode_header[] = "; generated by Slic3r "; - const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; - std::string firstline; - std::getline(ifs, firstline); - if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && - strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) - throw ConfigurationError("Not a PrusaSlicer / Slic3r PE generated g-code."); - } - ifs.seekg(0, ifs.end); - auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length); - ifs.seekg(file_length - data_length, ifs.beg); - std::vector data(size_t(data_length) + 1, 0); - ifs.read(data.data(), data_length); - ifs.close(); - - ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); - size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt); - if (key_value_pairs < 80) - throw ConfigurationError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); - return std::move(substitutions_ctxt.substitutions); - } catch (const ConfigurationError &e) { - throw ConfigurationError(format("Failed loading configuration from G-code \"%1%\": %2%", file, e.what())); - } -} - // Load the config keys from the given string. static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions) { diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index 45b432b5c7..027bf68492 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -119,6 +119,7 @@ public: wxString path; Vec2i win = {2, 2}; std::string err; + ConfigSubstitutions config_substitutions; priv(Plater *plt) : plater{plt} {} }; diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index ac4fa44e6f..77617fea1b 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -114,6 +114,22 @@ public: #endif +// Generic info dialog, used for displaying exceptions +class InfoDialog : public MsgDialog +{ +public: + InfoDialog(wxWindow *parent, const wxString &title, const wxString &msg); + InfoDialog(InfoDialog&&) = delete; + InfoDialog(const InfoDialog&) = delete; + InfoDialog&operator=(InfoDialog&&) = delete; + InfoDialog&operator=(const InfoDialog&) = delete; + virtual ~InfoDialog() = default; + +private: + wxString msg; +}; + + } } From 0e0c52ade3ad7c66b3317a774710eb00f7e12bff Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 13 Aug 2021 15:50:32 +0200 Subject: [PATCH 124/240] PR idx update --- resources/profiles/PrusaResearch.idx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index a0a7c1e2d6..dcb843a4a3 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -11,7 +11,7 @@ min_slic3r_version = 2.4.0-alpha0 1.3.0-alpha0 Disabled thick bridges, updated support settings. min_slic3r_version = 2.3.2-alpha0 1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S). -1.3.0 Added SL1S profiles. +1.3.0 Added SL1S SPEED profiles. min_slic3r_version = 2.3.0-rc1 1.2.8 Added multiple add:north and Extrudr filament profiles. 1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI. @@ -30,6 +30,7 @@ min_slic3r_version = 2.3.0-alpha4 1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. 1.2.0-alpha0 Added filament spool weights min_slic3r_version = 2.2.0-alpha3 +1.1.14 Updated firmware version. 1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. 1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. 1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. @@ -53,6 +54,7 @@ min_slic3r_version = 2.2.0-alpha0 1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. 1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 min_slic3r_version = 2.1.1-beta0 +1.0.12 Updated firmware version. 1.0.11 Updated firmware version. 1.0.10 Updated firmware version for MK2.5/S and MK3/S. 1.0.9 Updated firmware version for MK2.5/S and MK3/S. @@ -72,6 +74,7 @@ min_slic3r_version = 2.1.0-alpha0 1.0.0-alpha1 Added Prusament ASA profile 1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX min_slic3r_version = 1.42.0-alpha6 +0.8.11 Updated firmware version. 0.8.10 Updated firmware version. 0.8.9 Updated firmware version for MK2.5/S and MK3/S. 0.8.8 Updated firmware version for MK2.5/S and MK3/S. From 11b994a7021fc949ed9e18fa422648f7c7c7c457 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 27 Jul 2021 16:17:12 +0200 Subject: [PATCH 125/240] Rotfinder fixes wip --- src/libslic3r/SLA/Rotfinder.cpp | 74 ++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index b849212793..7c1e0daef1 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -58,29 +58,6 @@ T sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads) return execution::reduce(ex_tbb, from, to, initv, mergefn, accessfn, grainsize); } -// Try to guess the number of support points needed to support a mesh -double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr) -{ - if (mesh.its.vertices.empty()) return std::nan(""); - - auto accessfn = [&mesh, &tr](size_t fi) { - auto triangle = get_transformed_triangle(mesh, tr, fi); - Vec3f U = triangle[1] - triangle[0]; - Vec3f V = triangle[2] - triangle[0]; - Vec3f C = U.cross(V); - - // We should score against the alignment with the reference planes - return scaled(std::abs(C.dot(Vec3f::UnitX())) + - std::abs(C.dot(Vec3f::UnitY()))); - }; - - size_t facecount = mesh.its.indices.size(); - size_t Nthreads = std::thread::hardware_concurrency(); - double S = unscaled(sum_score(accessfn, facecount, Nthreads)); - - return S / facecount; -} - // Get area and normal of a triangle struct Facestats { Vec3f normal; @@ -96,18 +73,43 @@ struct Facestats { } }; +// Try to guess the number of support points needed to support a mesh +double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr) +{ + if (mesh.its.vertices.empty()) return std::nan(""); + + auto accessfn = [&mesh, &tr](size_t fi) { + Facestats fc{get_transformed_triangle(mesh, tr, fi)}; + + float score = fc.area + * (std::abs(fc.normal.dot(Vec3f::UnitX())) + + std::abs(fc.normal.dot(Vec3f::UnitY())) + + std::abs(fc.normal.dot(Vec3f::UnitZ()))); + + // We should score against the alignment with the reference planes + return scaled(score); + }; + + size_t facecount = mesh.its.indices.size(); + size_t Nthreads = std::thread::hardware_concurrency(); + double S = unscaled(sum_score(accessfn, facecount, Nthreads)); + + return S / facecount; +} + // The score function for a particular face inline double get_supportedness_score(const Facestats &fc) { // Simply get the angle (acos of dot product) between the face normal and // the DOWN vector. - float phi = 1. - std::acos(fc.normal.dot(DOWN)) / float(PI); + float cosphi = fc.normal.dot(DOWN); + float phi = 1.f - std::acos(cosphi) / float(PI); // Only consider faces that have slopes below 90 deg: - phi = phi * (phi >= 0.5f); + phi = (1.f + phi) * (phi >= 0.5f); // Make the huge slopes more significant than the smaller slopes - phi = phi * phi * phi; + phi = phi * phi; // Multiply with the area of the current face return fc.area * POINTS_PER_UNIT_AREA * phi; @@ -121,7 +123,7 @@ double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr) auto accessfn = [&mesh, &tr](size_t fi) { Facestats fc{get_transformed_triangle(mesh, tr, fi)}; - return get_supportedness_score(fc); + return scaled(get_supportedness_score(fc)); }; size_t facecount = mesh.its.indices.size(); @@ -164,7 +166,7 @@ float get_supportedness_onfloor_score(const TriangleMesh &mesh, Facestats fc{tri}; if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl) - return -fc.area * POINTS_PER_UNIT_AREA; + return -2 * fc.area * POINTS_PER_UNIT_AREA; return get_supportedness_score(fc); }; @@ -353,6 +355,15 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, TriangleMesh mesh = mo.raw_mesh(); mesh.require_shared_vertices(); + ModelInstance* mi = mo.instances[0]; + Vec3d rotation = mi->get_rotation(); + Transform3d trafo_instance = Geometry::assemble_transform(mi->get_offset().z() * Vec3d::UnitZ(), + rotation, + mi->get_scaling_factor(), + mi->get_mirror()); + + mesh.transform(trafo_instance); + // To keep track of the number of iterations unsigned status = 0; @@ -419,6 +430,13 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // Save the result rot = result.optimum; + std::cout << "Score was: " << result.score << std::endl; + +//auto rt = mo.instances[0]->get_rotation(); +//double score = get_supportedness_score(mesh, to_transform3f({rt(0), rt(1)})); +// std::cout << "Score was: " << score << std::endl; +// rot[0] = rt(0); +// rot[1] = rt(1); } return {rot[0], rot[1]}; From 8b5a63eaf8f42f18bf27d3365ed8c23a4c59a22b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 11:29:09 +0200 Subject: [PATCH 126/240] Make the input mesh transformation consistent. Also add some comments --- src/libslic3r/SLA/Rotfinder.cpp | 46 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 7c1e0daef1..daa3154d7c 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -105,7 +105,9 @@ inline double get_supportedness_score(const Facestats &fc) float cosphi = fc.normal.dot(DOWN); float phi = 1.f - std::acos(cosphi) / float(PI); - // Only consider faces that have slopes below 90 deg: + // Phi is raised by 1.0 to not be less than zero when squared in the next + // step. If phi is greater than 0.5 (slope is > 90 deg) make phi zero + // to not skip this face in the overall score. phi = (1.f + phi) * (phi >= 0.5f); // Make the huge slopes more significant than the smaller slopes @@ -285,6 +287,26 @@ std::array find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn) } // namespace +// Assemble the mesh with the correct transformation to be used in rotation +// optimization. +TriangleMesh get_mesh_to_rotate(const ModelObject &mo) +{ + TriangleMesh mesh = mo.raw_mesh(); + mesh.require_shared_vertices(); + + ModelInstance *mi = mo.instances[0]; + auto rotation = Vec3d::Zero(); + auto offset = Vec3d::Zero(); + Transform3d trafo_instance = Geometry::assemble_transform(offset, + rotation, + mi->get_scaling_factor(), + mi->get_mirror()); + + mesh.transform(trafo_instance); + + return mesh; +} + Vec2d find_best_misalignment_rotation(const ModelObject & mo, const RotOptimizeParams ¶ms) { @@ -295,8 +317,7 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo, // We will use only one instance of this converted mesh to examine different // rotations - TriangleMesh mesh = mo.raw_mesh(); - mesh.require_shared_vertices(); + TriangleMesh mesh = get_mesh_to_rotate(mo); // To keep track of the number of iterations int status = 0; @@ -352,17 +373,7 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // We will use only one instance of this converted mesh to examine different // rotations - TriangleMesh mesh = mo.raw_mesh(); - mesh.require_shared_vertices(); - - ModelInstance* mi = mo.instances[0]; - Vec3d rotation = mi->get_rotation(); - Transform3d trafo_instance = Geometry::assemble_transform(mi->get_offset().z() * Vec3d::UnitZ(), - rotation, - mi->get_scaling_factor(), - mi->get_mirror()); - - mesh.transform(trafo_instance); + TriangleMesh mesh = get_mesh_to_rotate(mo); // To keep track of the number of iterations unsigned status = 0; @@ -430,13 +441,6 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // Save the result rot = result.optimum; - std::cout << "Score was: " << result.score << std::endl; - -//auto rt = mo.instances[0]->get_rotation(); -//double score = get_supportedness_score(mesh, to_transform3f({rt(0), rt(1)})); -// std::cout << "Score was: " << score << std::endl; -// rot[0] = rt(0); -// rot[1] = rt(1); } return {rot[0], rot[1]}; From 62f8ab1cbe500b20a2a0ca5957d101d27a98be9c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 11:53:37 +0200 Subject: [PATCH 127/240] Add check of neighbors Add store triangle for debug purpose --- src/libslic3r/QuadricEdgeCollapse.cpp | 120 +++++++++++++++++++++++++- src/libslic3r/TriangleMesh.cpp | 42 +++++++++ src/libslic3r/TriangleMesh.hpp | 4 + 3 files changed, 164 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index ffe1e5ca78..52f68ee8d8 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -91,6 +91,119 @@ namespace QuadricEdgeCollapse { using namespace QuadricEdgeCollapse; +// store triangle surrounding to file +void store_surround(const char * obj_filename, + size_t triangle_index, + int depth, + const indexed_triangle_set &its, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + std::set triangles; + // triangle index, depth + using Item = std::pair; + std::queue process; + process.push({triangle_index, depth}); + + while (!process.empty()) { + Item item = process.front(); + process.pop(); + size_t ti = item.first; + auto it = triangles.find(ti); + if (it != triangles.end()) continue; + triangles.insert(ti); + if (item.second == 0) continue; + + const Vec3i &t = its.indices[ti]; + for (size_t i = 0; i < 3; ++i) { + const auto &v_info = v_infos[t[i]]; + for (size_t d = 0; d < v_info.count; ++d) { + size_t ei = v_info.start + d; + const auto & e_info = e_infos[ei]; + auto it = triangles.find(e_info.t_index); + if (it != triangles.end()) continue; + process.push({e_info.t_index, item.second - 1}); + } + } + } + + std::vector trs; + trs.reserve(triangles.size()); + for (size_t ti : triangles) trs.push_back(ti); + its_store_triangles(its, obj_filename, trs); + //its_write_obj(its,"original.obj"); +} + +bool check_neighbors(const indexed_triangle_set &its, + const TriangleInfos & t_infos, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + VertexInfos v_infos2(v_infos.size()); + size_t count_indices = 0; + + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + ++count_indices; + const Triangle &t = its.indices[ti]; + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos2[t[e]]; + ++v_info.count; // triangle count + } + } + + uint32_t triangle_start = 0; + for (VertexInfo &v_info : v_infos2) { + v_info.start = triangle_start; + triangle_start += v_info.count; + // set filled vertex to zero + v_info.count = 0; + } + + // create reference + EdgeInfos e_infos2(count_indices * 3); + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + const Triangle &t = its.indices[ti]; + for (size_t j = 0; j < 3; ++j) { + VertexInfo &v_info = v_infos2[t[j]]; + size_t ei = v_info.start + v_info.count; + assert(ei < e_infos2.size()); + EdgeInfo &e_info = e_infos2[ei]; + e_info.t_index = ti; + e_info.edge = j; + ++v_info.count; + } + } + + for (size_t vi = 0; vi < its.vertices.size(); vi++) { + const VertexInfo &v_info = v_infos[vi]; + if (v_info.is_deleted()) continue; + const VertexInfo &v_info2 = v_infos2[vi]; + if (v_info.count != v_info2.count) { + return false; + } + EdgeInfos eis; + eis.reserve(v_info.count); + std::copy(e_infos.begin() + v_info.start, + e_infos.begin() + v_info.start + v_info.count, + std::back_inserter(eis)); + auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { + return ei1.t_index < ei2.t_index; + }; + std::sort(eis.begin(), eis.end(), compare); + std::sort(e_infos2.begin() + v_info2.start, + e_infos2.begin() + v_info2.start + v_info2.count, + compare); + for (size_t ei = 0; ei < v_info.count; ++ei) { + if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { + return false; + } + } + } + return true; +} + void Slic3r::its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count, @@ -117,6 +230,9 @@ void Slic3r::its_quadric_edge_collapse( throw_on_cancel(); statusfn(status_init_size); + //its_store_triangle(its, "triangle.obj", 1182); + //store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos); + // convert from triangle index to mutable priority queue index std::vector ti_2_mpqi(its.indices.size(), {0}); auto setter = [&ti_2_mpqi](const Error &e, size_t index) { ti_2_mpqi[e.triangle_index] = index; }; @@ -237,8 +353,7 @@ void Slic3r::its_quadric_edge_collapse( } v_info0.q = q; - // fix neighbors - + // fix neighbors // vertex index of triangle 0 which is not vi0 nor vi1 uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3]; const Triangle &t1 = its.indices[ti1]; @@ -264,6 +379,7 @@ void Slic3r::its_quadric_edge_collapse( t_info1.set_deleted(); // triangle counter decrementation actual_triangle_count-=2; + assert(check_neighbors(its, t_infos, v_infos, e_infos)); } // compact triangle diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 360a8b14e1..d4baabc977 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -957,6 +957,48 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit) return removed; } +bool its_store_triangle(const indexed_triangle_set &its, + const char * obj_filename, + size_t triangle_index) +{ + if (its.indices.size() <= triangle_index) return false; + Vec3i t = its.indices[triangle_index]; + indexed_triangle_set its2; + its2.indices = {{0, 1, 2}}; + its2.vertices = {its.vertices[t[0]], its.vertices[t[1]], + its.vertices[t[2]]}; + return its_write_obj(its2, obj_filename); +} + +bool its_store_triangles(const indexed_triangle_set &its, + const char * obj_filename, + const std::vector & triangles) +{ + indexed_triangle_set its2; + its2.vertices.reserve(triangles.size() * 3); + its2.indices.reserve(triangles.size()); + std::map vertex_map; + for (auto ti : triangles) { + if (its.indices.size() <= ti) return false; + Vec3i t = its.indices[ti]; + Vec3i new_t; + for (size_t i = 0; i < 3; ++i) { + size_t vi = t[i]; + auto it = vertex_map.find(vi); + if (it != vertex_map.end()) { + new_t[i] = it->second; + continue; + } + size_t new_vi = its2.vertices.size(); + its2.vertices.push_back(its.vertices[vi]); + vertex_map[vi] = new_vi; + new_t[i] = new_vi; + } + its2.indices.push_back(new_t); + } + return its_write_obj(its2, obj_filename); +} + void its_shrink_to_fit(indexed_triangle_set &its) { its.indices.shrink_to_fit(); diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c8c7e0dd77..b7a1bebb15 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -140,6 +140,10 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = // Remove vertices, which none of the faces references. Return number of freed vertices. int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true); +// store part of index triangle set +bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index); +bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector& triangles); + std::vector its_split(const indexed_triangle_set &its); bool its_is_splittable(const indexed_triangle_set &its); From 0b2ee3bb460a6b7768c7b363589441012a0401a9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 16 Aug 2021 13:05:31 +0200 Subject: [PATCH 128/240] SLA supports: ask about saving changes when Esc is pressed --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 33 ++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 15f289251f..6d8ae36ab2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -447,7 +447,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::DiscardChanges) { - editing_mode_discard_changes(); + ask_about_changes_call_after([this](){ editing_mode_apply_changes(); }, + [this](){ editing_mode_discard_changes(); }); return true; } @@ -879,6 +880,22 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const +void GLGizmoSlaSupports::ask_about_changes_call_after(std::function on_yes, std::function on_no) +{ + wxGetApp().CallAfter([this, on_yes, on_no]() { + // Following is called through CallAfter, because otherwise there was a problem + // on OSX with the wxMessageDialog being shown several times when clicked into. + MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " + "edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL ); + int ret = dlg.ShowModal(); + if (ret == wxID_YES) + on_yes(); + else if (ret == wxID_NO) + on_no(); + }); +} + + void GLGizmoSlaSupports::on_set_state() { if (m_state == m_old_state) @@ -901,18 +918,8 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off bool will_ask = m_editing_mode && unsaved_changes() && on_is_activable(); if (will_ask) { - wxGetApp().CallAfter([this]() { - // Following is called through CallAfter, because otherwise there was a problem - // on OSX with the wxMessageDialog being shown several times when clicked into. - //wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " - MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " - "edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL ); - int ret = dlg.ShowModal(); - if (ret == wxID_YES) - editing_mode_apply_changes(); - else if (ret == wxID_NO) - editing_mode_discard_changes(); - }); + ask_about_changes_call_after([this](){ editing_mode_apply_changes(); }, + [this](){ editing_mode_discard_changes(); }); // refuse to be turned off so the gizmo is active when the CallAfter is executed m_state = m_old_state; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 6982ecf765..cb60c0e258 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -117,6 +117,7 @@ private: void auto_generate(); void switch_to_editing_mode(); void disable_editing_mode(); + void ask_about_changes_call_after(std::function on_yes, std::function on_no); protected: void on_set_state() override; From 21fd35d243ebb91e3c2a632b5d14d8e5cd521a36 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 13:04:39 +0200 Subject: [PATCH 129/240] Fix: Do not close dialog after preview --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 694eeadd4e..cdcf9ce1f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -194,19 +194,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi need_reload = false; // Reload visualization of mesh - change VBO, FBO on GPU - m_parent.reload_scene(true); // deactivate gizmo?? - GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); - gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); - + m_parent.reload_scene(true); if (state == State::close_on_end) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); - plater->changed_mesh(obj_index); // deactivate gizmo?? - // changed_mesh cause close(); - //close(); + plater->changed_mesh(obj_index); + close(); } - // change from simplifying | aply + // change from simplifying | apply state = State::settings; // Fix warning icon in object list From 268b06bdbb7eaa433c663413d9c9387c7fb15d67 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 15:30:33 +0200 Subject: [PATCH 130/240] fix position of window --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 47 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 2 + 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index cdcf9ce1f9..fa0b94200e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -53,10 +53,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi const int max_char_in_name = 25; create_gui_cfg(); - int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoCollapse; - m_imgui->begin(on_get_name(), flag); - const Selection &selection = m_parent.get_selection(); int object_idx = selection.get_object_idx(); ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; @@ -65,6 +61,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // Check selection of new volume // Do not reselect object when processing if (act_volume != volume && state == State::settings) { + bool change_window_position = (volume == nullptr); + // select different model + if (volume != nullptr && original_its.has_value()) { + set_its(*original_its); + } + obj_index = object_idx; // to remember correct object volume = act_volume; original_its = {}; @@ -72,13 +74,30 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi c.wanted_percent = 50.; // default value c.update_percent(tm.its.indices.size()); is_valid_result = false; - // set window position - ImVec2 pos = ImGui::GetMousePos(); - pos.x -= gui_cfg->window_offset; - pos.y -= gui_cfg->window_offset; - ImGui::SetWindowPos(pos, ImGuiCond_Always); + + if (change_window_position) { + ImVec2 pos = ImGui::GetMousePos(); + pos.x -= gui_cfg->window_offset; + pos.y -= gui_cfg->window_offset; + // minimal top left value + ImVec2 tl(gui_cfg->window_padding, gui_cfg->window_padding); + if (pos.x < tl.x) pos.x = tl.x; + if (pos.y < tl.y) pos.y = tl.y; + // maximal bottom right value + auto parent_size = m_parent.get_canvas_size(); + ImVec2 br( + parent_size.get_width() - (2 * gui_cfg->window_offset + gui_cfg->window_padding), + parent_size.get_height() - (2 * gui_cfg->window_offset + gui_cfg->window_padding)); + if (pos.x > br.x) pos.x = br.x; + if (pos.y > br.y) pos.y = br.y; + ImGui::SetNextWindowPos(pos, ImGuiCond_Always); + } } + int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse; + m_imgui->begin(on_get_name(), flag); + size_t triangle_count = volume->mesh().its.indices.size(); // already reduced mesh if (original_its.has_value()) @@ -211,8 +230,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } void GLGizmoSimplify::close() { - volume = nullptr; - // close gizmo == open it again GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager(); gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); @@ -293,6 +310,14 @@ bool GLGizmoSimplify::on_is_activable() const return !m_parent.get_selection().is_empty(); } +void GLGizmoSimplify::on_set_state() +{ + // Closing gizmo. e.g. selecting another one + if (m_state == GLGizmoBase::Off) { + volume = nullptr; + } +} + void GLGizmoSimplify::create_gui_cfg() { if (gui_cfg.has_value()) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index bd36373607..cefd1abf05 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -41,6 +41,7 @@ protected: virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual bool on_is_activable() const override; virtual bool on_is_selectable() const override { return false; } + virtual void on_set_state() override; private: void close(); @@ -78,6 +79,7 @@ private: int input_width = 100; int input_small_width = 80; int window_offset = 100; + int window_padding = 0; }; std::optional gui_cfg; void create_gui_cfg(); From 900814ff475a4c1b1c2411eb7ef13e5704012286 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 16:03:20 +0200 Subject: [PATCH 131/240] Add tooltips for combo items in rotation optimization dialog --- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 28 +++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 4 ---- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 27 ++++++++++-------------- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index a495db4f13..417a6a6443 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -497,9 +497,6 @@ void GLGizmoRotate3D::on_render() m_gizmos[Z].render(); } -const char * GLGizmoRotate3D::RotoptimzeWindow::options[RotoptimizeJob::get_methods_count()]; -bool GLGizmoRotate3D::RotoptimzeWindow::options_valid = false; - GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, State & state, const Alignment &alignment) @@ -517,19 +514,24 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, ImGui::PushItemWidth(200.f); - size_t methods_cnt = RotoptimizeJob::get_methods_count(); - if (!options_valid) { - for (size_t i = 0; i < methods_cnt; ++i) - options[i] = RotoptimizeJob::get_method_names()[i].c_str(); + if (ImGui::BeginCombo(_L("Choose goal").c_str(), RotoptimizeJob::get_method_name(state.method_id).c_str())) { + for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { + if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) { + state.method_id = i; + wxGetApp().app_config->set("sla_auto_rotate", + "method_id", + std::to_string(state.method_id)); + } - options_valid = true; + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(i).c_str()); + } + + ImGui::EndCombo(); } - int citem = state.method_id; - if (ImGui::Combo(_L("Choose goal").c_str(), &citem, options, methods_cnt) ) { - state.method_id = citem; - wxGetApp().app_config->set("sla_auto_rotate", "method_id", std::to_string(state.method_id)); - } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str()); ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index a51f900bf1..3245c4dbe8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -138,10 +138,6 @@ private: class RotoptimzeWindow { ImGuiWrapper *m_imgui = nullptr; - - static const char * options []; - static bool options_valid; - public: struct State { diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index 3144f3c3e6..3f10df0447 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -15,13 +15,13 @@ class RotoptimizeJob : public PlaterJob using FindFn = std::function; - struct FindMethod { std::string name; FindFn findfn; }; + struct FindMethod { std::string name; FindFn findfn; std::string descr; }; static inline const FindMethod Methods[] = { - { L("Best surface quality"), sla::find_best_misalignment_rotation }, - { L("Least supports"), sla::find_least_supports_rotation }, + { L("Best surface quality"), sla::find_best_misalignment_rotation, L("Optimize object rotation for best surface quality.") }, + { L("Least supports"), sla::find_least_supports_rotation, L("Optimize object rotation to have minimum amount of overhangs needing support structures.") }, // Just a min area bounding box that is done for all methods anyway. - { L("Z axis only"), nullptr } + { L("Z axis only"), nullptr, L("Rotate the object only in Z axis to have the smallest bounding box.") } }; size_t m_method_id = 0; @@ -52,20 +52,15 @@ public: void finalize() override; static constexpr size_t get_methods_count() { return std::size(Methods); } - static const auto & get_method_names() + + static std::string get_method_name(size_t i) { - static bool m_method_names_valid = false; - static std::array m_method_names; + return _utf8(Methods[i].name); + } - if (!m_method_names_valid) { - - for (size_t i = 0; i < std::size(Methods); ++i) - m_method_names[i] = _utf8(Methods[i].name); - - m_method_names_valid = true; - } - - return m_method_names; + static std::string get_method_description(size_t i) + { + return _utf8(Methods[i].descr); } }; From c37bb0cdd1bf463c0afab4d6d4c889d743c26544 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 16:42:56 +0200 Subject: [PATCH 132/240] Extend tooltip texts --- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index 3f10df0447..bb4310e638 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -17,12 +17,19 @@ class RotoptimizeJob : public PlaterJob struct FindMethod { std::string name; FindFn findfn; std::string descr; }; - static inline const FindMethod Methods[] = { - { L("Best surface quality"), sla::find_best_misalignment_rotation, L("Optimize object rotation for best surface quality.") }, - { L("Least supports"), sla::find_least_supports_rotation, L("Optimize object rotation to have minimum amount of overhangs needing support structures.") }, - // Just a min area bounding box that is done for all methods anyway. - { L("Z axis only"), nullptr, L("Rotate the object only in Z axis to have the smallest bounding box.") } - }; + static inline const FindMethod Methods[] + = {{L("Best surface quality"), + sla::find_best_misalignment_rotation, + L("Optimize object rotation for best surface quality.")}, + {L("Least supports"), + sla::find_least_supports_rotation, + L("Optimize object rotation to have minimum amount of overhangs needing support " + "structures.\nNote that this method will try to find the best surface of the object " + "for touching the print bed if no elevation is set.")}, + // Just a min area bounding box that is done for all methods anyway. + {L("Z axis only"), + nullptr, + L("Rotate the object only in Z axis to have the smallest bounding box.")}}; size_t m_method_id = 0; float m_accuracy = 0.75; From c04856e049221312a87761df0e194488ba95b5c6 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 18:04:38 +0200 Subject: [PATCH 133/240] Extend simplify test to chack distance of each triangle center and each vertices. Both simplify to origin and vice versa --- tests/libslic3r/test_indexed_triangle_set.cpp | 71 +++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 0e08749bf0..a3996e6519 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -165,29 +165,51 @@ std::vector its_sample_surface(const indexed_triangle_set &its, #include "libslic3r/AABBTreeIndirect.hpp" -// return Average abs distance to original -float compare(const indexed_triangle_set &original, - const indexed_triangle_set &simplified, - double sample_per_mm2) +struct CompareConfig +{ + float max_distance = 3.f; + float max_average_distance = 2.f; +}; + +bool is_similar(const indexed_triangle_set &from, + const indexed_triangle_set &to, + const CompareConfig &cfg) { // create ABBTree auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( - original.vertices, original.indices); + from.vertices, from.indices); + float sum_distance = 0.f; + float max_distance = 0.f; - unsigned int init = 0; - std::mt19937 rnd(init); - auto samples = its_sample_surface(simplified, sample_per_mm2, rnd); - - float sumDistance = 0; - for (const Vec3f &sample : samples) { + auto collect_distances = [&](const Vec3f &surface_point) { size_t hit_idx; Vec3f hit_point; - float distance2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - original.vertices, original.indices, tree, sample, hit_idx, - hit_point); - sumDistance += sqrt(distance2); + float distance2 = + AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + from.vertices, from.indices, tree, surface_point, hit_idx, hit_point); + float distance = sqrt(distance2); + if (max_distance < distance) max_distance = distance; + sum_distance += distance; + }; + + for (const Vec3f &vertex : to.vertices) { + collect_distances(vertex); } - return sumDistance / samples.size(); + + for (const Vec3i &t : to.indices) { + Vec3f center(0,0,0); + for (size_t i = 0; i < 3; ++i) { + center += to.vertices[t[i]] / 3; + } + collect_distances(center); + } + + size_t count = to.vertices.size() + to.indices.size(); + float avg_distance = sum_distance / count; + if (avg_distance > cfg.max_average_distance || + max_distance > cfg.max_distance) + return false; + return true; } TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") @@ -226,8 +248,12 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") (v[i] > v4[i] && v[i] < v2[i]); CHECK(is_between); } - float avg_distance = compare(its_, its, 10); - CHECK(avg_distance < 8e-3f); + CompareConfig cfg; + cfg.max_average_distance = 0.014f; + cfg.max_distance = 0.75f; + + CHECK(is_similar(its, its_, cfg)); + CHECK(is_similar(its_, its, cfg)); } #include "test_utils.hpp" @@ -244,8 +270,13 @@ TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); CHECK(fabs(original_volume - volume) < 33.); - float avg_distance = compare(mesh.its, its, 10); - CHECK(avg_distance < 0.022f); // 0.02022 | 0.0199614074 + + CompareConfig cfg; + cfg.max_average_distance = 0.043f; + cfg.max_distance = 0.32f; + + CHECK(is_similar(mesh.its, its, cfg)); + CHECK(is_similar(its, mesh.its, cfg)); } TEST_CASE("Simplify trouble case", "[its]") From 090728b9d5c1279650bc585f9be7c1b5fb4608fd Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 17 Aug 2021 08:58:45 +0200 Subject: [PATCH 134/240] Add private member prefix m_ --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 230 +++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 48 ++--- 2 files changed, 142 insertions(+), 136 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index fa0b94200e..23ab4ef13e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -18,17 +18,17 @@ GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, const std::string &icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, -1) - , state(State::settings) - , is_valid_result(false) - , progress(0) - , volume(nullptr) - , obj_index(0) - , need_reload(false) + , m_state(State::settings) + , m_is_valid_result(false) + , m_progress(0) + , m_volume(nullptr) + , m_obj_index(0) + , m_need_reload(false) {} GLGizmoSimplify::~GLGizmoSimplify() { - state = State::canceling; - if (worker.joinable()) worker.join(); + m_state = State::canceling; + if (m_worker.joinable()) m_worker.join(); } bool GLGizmoSimplify::on_init() @@ -60,34 +60,34 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // Check selection of new volume // Do not reselect object when processing - if (act_volume != volume && state == State::settings) { - bool change_window_position = (volume == nullptr); + if (act_volume != m_volume && m_state == State::settings) { + bool change_window_position = (m_volume == nullptr); // select different model - if (volume != nullptr && original_its.has_value()) { - set_its(*original_its); + if (m_volume != nullptr && m_original_its.has_value()) { + set_its(*m_original_its); } - obj_index = object_idx; // to remember correct object - volume = act_volume; - original_its = {}; - const TriangleMesh &tm = volume->mesh(); - c.wanted_percent = 50.; // default value - c.update_percent(tm.its.indices.size()); - is_valid_result = false; + m_obj_index = object_idx; // to remember correct object + m_volume = act_volume; + m_original_its = {}; + const TriangleMesh &tm = m_volume->mesh(); + m_configuration.wanted_percent = 50.; // default value + m_configuration.update_percent(tm.its.indices.size()); + m_is_valid_result = false; if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); - pos.x -= gui_cfg->window_offset; - pos.y -= gui_cfg->window_offset; + pos.x -= m_gui_cfg->window_offset; + pos.y -= m_gui_cfg->window_offset; // minimal top left value - ImVec2 tl(gui_cfg->window_padding, gui_cfg->window_padding); + ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding); if (pos.x < tl.x) pos.x = tl.x; if (pos.y < tl.y) pos.y = tl.y; // maximal bottom right value auto parent_size = m_parent.get_canvas_size(); ImVec2 br( - parent_size.get_width() - (2 * gui_cfg->window_offset + gui_cfg->window_padding), - parent_size.get_height() - (2 * gui_cfg->window_offset + gui_cfg->window_padding)); + parent_size.get_width() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding), + parent_size.get_height() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding)); if (pos.x > br.x) pos.x = br.x; if (pos.y > br.y) pos.y = br.y; ImGui::SetNextWindowPos(pos, ImGuiCond_Always); @@ -98,98 +98,100 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGuiWindowFlags_NoCollapse; m_imgui->begin(on_get_name(), flag); - size_t triangle_count = volume->mesh().its.indices.size(); + size_t triangle_count = m_volume->mesh().its.indices.size(); // already reduced mesh - if (original_its.has_value()) - triangle_count = original_its->indices.size(); + if (m_original_its.has_value()) + triangle_count = m_original_its->indices.size(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); - ImGui::SameLine(gui_cfg->top_left_width); - std::string name = volume->name; + ImGui::SameLine(m_gui_cfg->top_left_width); + std::string name = m_volume->name; if (name.length() > max_char_in_name) name = name.substr(0, max_char_in_name-3) + "..."; m_imgui->text(name); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); - ImGui::SameLine(gui_cfg->top_left_width); + ImGui::SameLine(m_gui_cfg->top_left_width); m_imgui->text(std::to_string(triangle_count)); ImGui::Separator(); ImGui::Text(_L("Limit by triangles").c_str()); - ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SameLine(m_gui_cfg->bottom_left_width); // First initialization + fix triangle count - if (m_imgui->checkbox("##UseCount", c.use_count)) { - if (!c.use_count) c.use_error = true; - is_valid_result = false; + if (m_imgui->checkbox("##UseCount", m_configuration.use_count)) { + if (!m_configuration.use_count) m_configuration.use_error = true; + m_is_valid_result = false; } - m_imgui->disabled_begin(!c.use_count); + m_imgui->disabled_begin(!m_configuration.use_count); ImGui::Text(_L("Triangle count").c_str()); - ImGui::SameLine(gui_cfg->bottom_left_width); - int wanted_count = c.wanted_count; - ImGui::SetNextItemWidth(gui_cfg->input_width); + ImGui::SameLine(m_gui_cfg->bottom_left_width); + int wanted_count = m_configuration.wanted_count; + ImGui::SetNextItemWidth(m_gui_cfg->input_width); if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) { - c.wanted_count = static_cast(wanted_count); - if (c.wanted_count < min_triangle_count) - c.wanted_count = min_triangle_count; - if (c.wanted_count > triangle_count) - c.wanted_count = triangle_count; - c.update_count(triangle_count); - is_valid_result = false; + m_configuration.wanted_count = static_cast(wanted_count); + if (m_configuration.wanted_count < min_triangle_count) + m_configuration.wanted_count = min_triangle_count; + if (m_configuration.wanted_count > triangle_count) + m_configuration.wanted_count = triangle_count; + m_configuration.update_count(triangle_count); + m_is_valid_result = false; } ImGui::Text(_L("Ratio").c_str()); - ImGui::SameLine(gui_cfg->bottom_left_width); - ImGui::SetNextItemWidth(gui_cfg->input_small_width); - const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f"); - float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f); - if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) { - if (c.wanted_percent > 100.f) c.wanted_percent = 100.f; - c.update_percent(triangle_count); - if (c.wanted_count < min_triangle_count) { - c.wanted_count = min_triangle_count; - c.update_count(triangle_count); + ImGui::SameLine(m_gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); + const char * precision = (m_configuration.wanted_percent > 10)? "%.0f": + ((m_configuration.wanted_percent > 1)? "%.1f":"%.2f"); + float step = (m_configuration.wanted_percent > 10)? 1.f: + ((m_configuration.wanted_percent > 1)? 0.1f : 0.01f); + if (ImGui::InputFloat("%", &m_configuration.wanted_percent, step, 10*step, precision)) { + if (m_configuration.wanted_percent > 100.f) m_configuration.wanted_percent = 100.f; + m_configuration.update_percent(triangle_count); + if (m_configuration.wanted_count < min_triangle_count) { + m_configuration.wanted_count = min_triangle_count; + m_configuration.update_count(triangle_count); } - is_valid_result = false; + m_is_valid_result = false; } m_imgui->disabled_end(); // use_count ImGui::NewLine(); ImGui::Text(_L("Limit by error").c_str()); - ImGui::SameLine(gui_cfg->bottom_left_width); - if (m_imgui->checkbox("##UseError", c.use_error)) { - if (!c.use_error) c.use_count = true; - is_valid_result = false; + ImGui::SameLine(m_gui_cfg->bottom_left_width); + if (m_imgui->checkbox("##UseError", m_configuration.use_error)) { + if (!m_configuration.use_error) m_configuration.use_count = true; + m_is_valid_result = false; } - m_imgui->disabled_begin(!c.use_error); + m_imgui->disabled_begin(!m_configuration.use_error); ImGui::Text(_L("Max. error").c_str()); - ImGui::SameLine(gui_cfg->bottom_left_width); - ImGui::SetNextItemWidth(gui_cfg->input_small_width); - if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) { - if (c.max_error < 0.f) c.max_error = 0.f; - is_valid_result = false; + ImGui::SameLine(m_gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); + if (ImGui::InputFloat("##maxError", &m_configuration.max_error, 0.01f, .1f, "%.2f")) { + if (m_configuration.max_error < 0.f) m_configuration.max_error = 0.f; + m_is_valid_result = false; } m_imgui->disabled_end(); // use_error - if (state == State::settings) { + if (m_state == State::settings) { if (m_imgui->button(_L("Cancel"))) { - if (original_its.has_value()) { - set_its(*original_its); - state = State::close_on_end; + if (m_original_its.has_value()) { + set_its(*m_original_its); + m_state = State::close_on_end; } else { close(); } } - ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SameLine(m_gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { - state = State::simplifying; + m_state = State::simplifying; // simplify but not aply on mesh process(); } ImGui::SameLine(); if (m_imgui->button(_L("Apply"))) { - if (!is_valid_result) { - state = State::close_on_end; + if (!m_is_valid_result) { + m_state = State::close_on_end; process(); } else { // use preview and close @@ -197,35 +199,35 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } } } else { - m_imgui->disabled_begin(state == State::canceling); - if (m_imgui->button(_L("Cancel"))) state = State::canceling; + m_imgui->disabled_begin(m_state == State::canceling); + if (m_imgui->button(_L("Cancel"))) m_state = State::canceling; m_imgui->disabled_end(); - ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SameLine(m_gui_cfg->bottom_left_width); // draw progress bar char buf[32]; - sprintf(buf, L("Process %d / 100"), progress); - ImGui::ProgressBar(progress / 100., ImVec2(gui_cfg->input_width, 0.f), buf); + sprintf(buf, L("Process %d / 100"), m_progress); + ImGui::ProgressBar(m_progress / 100., ImVec2(m_gui_cfg->input_width, 0.f), buf); } m_imgui->end(); - if (need_reload) { - need_reload = false; + if (m_need_reload) { + m_need_reload = false; // Reload visualization of mesh - change VBO, FBO on GPU m_parent.reload_scene(true); - if (state == State::close_on_end) { + if (m_state == State::close_on_end) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); - plater->changed_mesh(obj_index); + plater->changed_mesh(m_obj_index); close(); } // change from simplifying | apply - state = State::settings; + m_state = State::settings; // Fix warning icon in object list - wxGetApp().obj_list()->update_item_error_icon(obj_index, -1); + wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1); } } @@ -243,51 +245,53 @@ void GLGizmoSimplify::process() const char* what() const throw() { return L("Model simplification has been canceled"); } }; - if (!original_its.has_value()) - original_its = volume->mesh().its; // copy + if (!m_original_its.has_value()) + m_original_its = m_volume->mesh().its; // copy auto plater = wxGetApp().plater(); - plater->take_snapshot(_L("Simplify ") + volume->name); - plater->clear_before_change_mesh(obj_index); - progress = 0; - if (worker.joinable()) worker.join(); - worker = std::thread([&]() { + plater->take_snapshot(_L("Simplify ") + m_volume->name); + plater->clear_before_change_mesh(m_obj_index); + m_progress = 0; + if (m_worker.joinable()) m_worker.join(); + m_worker = std::thread([&]() { // store original triangles - uint32_t triangle_count = (c.use_count) ? c.wanted_count : 0; - float max_error = (c.use_error) ? c.max_error : std::numeric_limits::max(); + uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0; + float max_error = (m_configuration.use_error) ? + m_configuration.max_error : std::numeric_limits::max(); std::function throw_on_cancel = [&]() { - if (state == State::canceling) { + if (m_state == State::canceling) { throw SimplifyCanceledException(); } }; std::function statusfn = [&](int percent) { - progress = percent; + m_progress = percent; m_parent.schedule_extra_frame(0); }; indexed_triangle_set collapsed; - if (last_error.has_value()) { + if (m_last_error.has_value()) { // is chance to continue with last reduction - const indexed_triangle_set &its = volume->mesh().its; + const indexed_triangle_set &its = m_volume->mesh().its; uint32_t last_triangle_count = static_cast(its.indices.size()); - if ((!c.use_count || triangle_count <= last_triangle_count) && - (!c.use_error || c.max_error <= *last_error)) { + if ((!m_configuration.use_count || triangle_count <= last_triangle_count) && + (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { collapsed = its; // small copy } else { - collapsed = *original_its; // copy + collapsed = *m_original_its; // copy } } else { - collapsed = *original_its; // copy + collapsed = *m_original_its; // copy } try { its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); - is_valid_result = true; - last_error = max_error; + m_is_valid_result = true; + m_last_error = max_error; } catch (SimplifyCanceledException &) { - state = State::settings; + // set state out of main thread + m_state = State::settings; } // need to render last status fn // without sleep it freezes until mouse move @@ -299,10 +303,10 @@ void GLGizmoSimplify::process() void GLGizmoSimplify::set_its(indexed_triangle_set &its) { auto tm = std::make_unique(its); tm->repair(); - volume->set_mesh(std::move(tm)); - volume->set_new_unique_id(); - volume->get_object()->invalidate_bounding_box(); - need_reload = true; + m_volume->set_mesh(std::move(tm)); + m_volume->set_new_unique_id(); + m_volume->get_object()->invalidate_bounding_box(); + m_need_reload = true; } bool GLGizmoSimplify::on_is_activable() const @@ -313,13 +317,13 @@ bool GLGizmoSimplify::on_is_activable() const void GLGizmoSimplify::on_set_state() { // Closing gizmo. e.g. selecting another one - if (m_state == GLGizmoBase::Off) { - volume = nullptr; + if (GLGizmoBase::m_state == GLGizmoBase::Off) { + m_volume = nullptr; } } void GLGizmoSimplify::create_gui_cfg() { - if (gui_cfg.has_value()) return; + if (m_gui_cfg.has_value()) return; int space_size = m_imgui->calc_text_size(":MM").x; GuiCfg cfg; @@ -337,7 +341,7 @@ void GLGizmoSimplify::create_gui_cfg() { cfg.input_width = cfg.bottom_left_width; cfg.input_small_width = cfg.input_width * 0.8; cfg.window_offset = cfg.input_width; - gui_cfg = cfg; + m_gui_cfg = cfg; } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index cefd1abf05..57174b7c12 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -10,26 +10,7 @@ namespace Slic3r { namespace GUI { class GLGizmoSimplify : public GLGizmoBase -{ - enum class State { - settings, - simplifying, // start processing - canceling, // canceled - successfull, // successful simplified - close_on_end - } state; - - bool is_valid_result; // differ what to do in apply - int progress; - - ModelVolume *volume; - size_t obj_index; - std::optional original_its; - - std::optional last_error; // for use previous reduction - - bool need_reload; // after simplify, glReload must be on main thread - std::thread worker; +{ public: GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); virtual ~GLGizmoSimplify(); @@ -47,6 +28,28 @@ private: void close(); void process(); void set_its(indexed_triangle_set &its); + void create_gui_cfg(); + + bool m_is_valid_result; // differ what to do in apply + volatile int m_progress; // percent of done work + ModelVolume *m_volume; // + size_t m_obj_index; + + std::optional m_original_its; + std::optional m_last_error; // for use previous reduction + + volatile bool m_need_reload; // after simplify, glReload must be on main thread + std::thread m_worker; + + enum class State { + settings, + simplifying, // start processing + canceling, // canceled + successfull, // successful simplified + close_on_end + }; + volatile State m_state; + struct Configuration { bool use_count = true; @@ -67,7 +70,7 @@ private: wanted_count = static_cast( std::round(triangle_count * wanted_percent / 100.f)); } - } c; + } m_configuration; // This configs holds GUI layout size given by translated texts. // etc. When language changes, GUI is recreated and this class constructed again, @@ -81,8 +84,7 @@ private: int window_offset = 100; int window_padding = 0; }; - std::optional gui_cfg; - void create_gui_cfg(); + std::optional m_gui_cfg; }; } // namespace GUI From 5a005e8e0afa60f34073f5aa038252e552c98c6b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 16 Aug 2021 13:26:27 +0200 Subject: [PATCH 135/240] Slightly changed 'Some objects missing' warning text --- 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 529056e999..227e5ae4e5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6380,7 +6380,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) case EWarning::ObjectOutside: text = _u8L("An object outside the print area was detected."); break; case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break; case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break; - case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible."); break; + case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible during editing."); break; case EWarning::ObjectClashed: text = _u8L("An object outside the print area was detected.\n" "Resolve the current problem to continue slicing."); From 08e2e29afdd459b0b392b2a082e1e7b599cfe294 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 Aug 2021 13:53:22 +0200 Subject: [PATCH 136/240] ObjectList: Implemented interface for delete InfoItem --- src/slic3r/GUI/GUI_ObjectList.cpp | 11 +++++++---- src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/ObjectDataViewModel.cpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 07119b8de1..138eb82968 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1778,10 +1778,8 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) del_layers_from_object(obj_idx); else if (type & itLayer && obj_idx != -1) del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); - else if (type & itInfo && obj_idx != -1) { - Unselect(item); - Select(parent); - } + else if (type & itInfo && obj_idx != -1) + del_info_item(obj_idx, m_objects_model->GetInfoItemType(item)); else if (idx == -1) return; else if (!del_subobject_from_object(obj_idx, idx, type)) @@ -1795,6 +1793,11 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) update_info_items(obj_idx); } +void ObjectList::del_info_item(const int obj_idx, InfoItemType type) +{ + // ToDo lmFIXME :) +} + void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) { const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 71730b2c06..0fbad19192 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -254,6 +254,7 @@ public: void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range); void del_layers_from_object(const int obj_idx); bool del_subobject_from_object(const int obj_idx, const int idx, const int type); + void del_info_item(const int obj_idx, InfoItemType type); void split(); void merge(bool to_multipart_object); void layers_editing(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 96d7ca8aed..9417364ef1 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1141,7 +1141,7 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type if (!node || node->GetIdx() <-1 || ( node->GetIdx() == -1 && - !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) + !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot | itInfo)) ) ) return; From dd1e1307bebc2558dd79e05389401854f3e36879 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 16 Aug 2021 09:48:29 +0200 Subject: [PATCH 137/240] Use Del key to delete custom supports etc. from the object list --- src/slic3r/GUI/GUI_ObjectList.cpp | 43 ++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 138eb82968..318705e09e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1795,7 +1795,48 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) void ObjectList::del_info_item(const int obj_idx, InfoItemType type) { - // ToDo lmFIXME :) + Plater* plater = wxGetApp().plater(); + GLCanvas3D* cnv = plater->canvas3D(); + + switch (type) { + case InfoItemType::CustomSupports: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, _L("Remove paint-on supports")); + for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) + mv->supported_facets.clear(); + break; + + case InfoItemType::CustomSeam: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, _L("Remove paint-on seam")); + for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) + mv->seam_facets.clear(); + break; + + case InfoItemType::MmuSegmentation: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, _L("Remove Multi Material painting")); + for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) + mv->mmu_segmentation_facets.clear(); + break; + + case InfoItemType::Sinking: + Plater::TakeSnapshot(plater, _L("Shift objects to bed")); + (*m_objects)[obj_idx]->ensure_on_bed(); + cnv->reload_scene(true, true); + break; + + case InfoItemType::VariableLayerHeight: + Plater::TakeSnapshot(plater, _L("Remove variable layer height")); + (*m_objects)[obj_idx]->layer_height_profile.clear(); + if (cnv->is_layers_editing_enabled()) + //cnv->post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); + cnv->force_main_toolbar_left_action(cnv->get_main_toolbar_item_id("layersediting")); + break; + + case InfoItemType::Undef : assert(false); break; + } + cnv->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) From 86c1e5c50fa145cdd23a2578cecacf891d827a3b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 17 Aug 2021 15:09:34 +0200 Subject: [PATCH 138/240] Default values for brim_offset anf elefant_foot_compensation set to zero --- src/libslic3r/PrintConfig.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 64afcf298d..e64824d0db 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -295,7 +295,7 @@ void PrintConfigDef::init_common_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.2)); + def->set_default_value(new ConfigOptionFloat(0.)); // Options used by physical printers @@ -495,7 +495,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.1f)); + def->set_default_value(new ConfigOptionFloat(0.f)); def = this->add("clip_multipart_objects", coBool); def->label = L("Clip multi-part objects"); From 11c91d781e4c56c5047a692f743d5f65a938c561 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 17 Aug 2021 15:28:08 +0200 Subject: [PATCH 139/240] FIX: extra frame request Do not freeze bargraph in Siplify dialog when no mouse move. --- src/slic3r/GUI/GLCanvas3D.cpp | 19 ++++++----------- src/slic3r/GUI/GLCanvas3D.hpp | 4 +--- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 25 +++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 9 ++++---- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 529056e999..96cf015ea7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2780,11 +2780,10 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_render_timer(wxTimerEvent& evt) { - // no need to do anything here - // right after this event is recieved, idle event is fired - - //m_dirty = true; - //wxWakeUpIdle(); + m_dirty = true; + // wxWakeUpIdle(); + // no need to wake up idle + // right after this event, idle event is fired } @@ -2802,21 +2801,15 @@ void GLCanvas3D::schedule_extra_frame(int miliseconds) return; } } - // Start timer - int64_t now = timestamp_now(); + int remaining_time = m_render_timer.GetInterval(); // Timer is not running - if (! m_render_timer.IsRunning()) { - m_extra_frame_requested_delayed = miliseconds; + if (!m_render_timer.IsRunning()) { m_render_timer.StartOnce(miliseconds); - m_render_timer_start = now; // Timer is running - restart only if new period is shorter than remaning period } else { - const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now; if (miliseconds + 20 < remaining_time) { m_render_timer.Stop(); - m_extra_frame_requested_delayed = miliseconds; m_render_timer.StartOnce(miliseconds); - m_render_timer_start = now; } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d9cd55e35b..ba8430c07f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -463,15 +463,13 @@ private: std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() - bool m_extra_frame_requested; - int m_extra_frame_requested_delayed { std::numeric_limits::max() }; + bool m_extra_frame_requested; bool m_event_handlers_bound{ false }; GLVolumeCollection m_volumes; GCodeViewer m_gcode_viewer; RenderTimer m_render_timer; - int64_t m_render_timer_start; Selection m_selection; const DynamicPrintConfig* m_config; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 23ab4ef13e..06a999cad6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -184,7 +184,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(m_gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { - m_state = State::simplifying; + m_state = State::preview; // simplify but not aply on mesh process(); } @@ -263,23 +263,18 @@ void GLGizmoSimplify::process() if (m_state == State::canceling) { throw SimplifyCanceledException(); } - }; + }; std::function statusfn = [&](int percent) { m_progress = percent; m_parent.schedule_extra_frame(0); }; indexed_triangle_set collapsed; - if (m_last_error.has_value()) { - // is chance to continue with last reduction - const indexed_triangle_set &its = m_volume->mesh().its; - uint32_t last_triangle_count = static_cast(its.indices.size()); - if ((!m_configuration.use_count || triangle_count <= last_triangle_count) && - (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { - collapsed = its; // small copy - } else { - collapsed = *m_original_its; // copy - } + if (m_last_error.has_value() && m_last_count.has_value() && + (!m_configuration.use_count || triangle_count <= *m_last_count) && + (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { + // continue from last reduction - speed up + collapsed = m_volume->mesh().its; // small copy } else { collapsed = *m_original_its; // copy } @@ -288,14 +283,14 @@ void GLGizmoSimplify::process() its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); m_is_valid_result = true; + m_last_count = triangle_count; // need to store last requirement, collapsed count could be count-1 m_last_error = max_error; } catch (SimplifyCanceledException &) { // set state out of main thread + m_last_error = {}; m_state = State::settings; } - // need to render last status fn - // without sleep it freezes until mouse move - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + // need to render last status fn to change bar graph to buttons m_parent.schedule_extra_frame(0); }); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 57174b7c12..5a84cabad2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -36,17 +36,18 @@ private: size_t m_obj_index; std::optional m_original_its; + std::optional m_last_error; // for use previous reduction + std::optional m_last_count; volatile bool m_need_reload; // after simplify, glReload must be on main thread std::thread m_worker; enum class State { settings, - simplifying, // start processing - canceling, // canceled - successfull, // successful simplified - close_on_end + preview, // simplify to show preview + close_on_end, // simplify with close on end + canceling // after button click, before canceled }; volatile State m_state; From 1a2e58e521b030f14daf78cede926131e3b6738c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Aug 2021 15:35:52 +0200 Subject: [PATCH 140/240] Add better defined names for orientation optimizer goals --- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 2 +- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 417a6a6443..04e08adc15 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -512,7 +512,7 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, y = std::min(y, alignment.bottom_limit - win_h); ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - ImGui::PushItemWidth(200.f); + ImGui::PushItemWidth(300.f); if (ImGui::BeginCombo(_L("Choose goal").c_str(), RotoptimizeJob::get_method_name(state.method_id).c_str())) { for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index bb4310e638..edabb7caed 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -21,13 +21,13 @@ class RotoptimizeJob : public PlaterJob = {{L("Best surface quality"), sla::find_best_misalignment_rotation, L("Optimize object rotation for best surface quality.")}, - {L("Least supports"), + {L("Reduced overhang slopes"), sla::find_least_supports_rotation, L("Optimize object rotation to have minimum amount of overhangs needing support " "structures.\nNote that this method will try to find the best surface of the object " "for touching the print bed if no elevation is set.")}, // Just a min area bounding box that is done for all methods anyway. - {L("Z axis only"), + {L("Smallest bounding box (Z axis only)"), nullptr, L("Rotate the object only in Z axis to have the smallest bounding box.")}}; From 24815381d2e3f266e30a7cb6aa08bed0e695ec41 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Aug 2021 15:37:41 +0200 Subject: [PATCH 141/240] Some improvements to "less supports" optimizer --- src/libslic3r/SLA/Rotfinder.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index daa3154d7c..d18d2fe6bb 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -105,16 +105,13 @@ inline double get_supportedness_score(const Facestats &fc) float cosphi = fc.normal.dot(DOWN); float phi = 1.f - std::acos(cosphi) / float(PI); - // Phi is raised by 1.0 to not be less than zero when squared in the next - // step. If phi is greater than 0.5 (slope is > 90 deg) make phi zero - // to not skip this face in the overall score. - phi = (1.f + phi) * (phi >= 0.5f); - // Make the huge slopes more significant than the smaller slopes - phi = phi * phi; + phi = phi * phi * phi; - // Multiply with the area of the current face - return fc.area * POINTS_PER_UNIT_AREA * phi; + // Multiply with the square root of face area of the current face, + // the area is less important as it grows. + // This makes many smaller overhangs a bigger impact. + return std::sqrt(fc.area) * POINTS_PER_UNIT_AREA * phi; } // Try to guess the number of support points needed to support a mesh @@ -124,7 +121,6 @@ double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr) auto accessfn = [&mesh, &tr](size_t fi) { Facestats fc{get_transformed_triangle(mesh, tr, fi)}; - return scaled(get_supportedness_score(fc)); }; @@ -349,7 +345,7 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo, // We are searching rotations around only two axes x, y. Thus the // problem becomes a 2 dimensional optimization task. // We can specify the bounds for a dimension in the following way: - auto bounds = opt::bounds({ {-PI/2, PI/2}, {-PI/2, PI/2} }); + auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); auto result = solver.to_max().optimize( [&mesh, &statusfn] (const XYRotation &rot) From e645f4a4eeff19b40268ad62307f845453a972c7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Aug 2021 08:59:56 +0200 Subject: [PATCH 142/240] Fixed cut contours after cutting an object in gizmo cut --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 707726e080..7d3f5c3dfb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -209,8 +209,13 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) m_imgui->end(); - if (cut_clicked && (m_keep_upper || m_keep_lower)) + if (cut_clicked && (m_keep_upper || m_keep_lower)) { perform_cut(m_parent.get_selection()); + m_cut_contours.cut_z = 0.0f; + m_cut_contours.object_idx = -1; + m_cut_contours.instance_idx = -1; + m_cut_contours.contours.reset(); + } } void GLGizmoCut::set_cut_z(double cut_z) @@ -308,9 +313,8 @@ void GLGizmoCut::update_contours() m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); } } - else if (box.center() != m_cut_contours.position) { + else if (box.center() != m_cut_contours.position) m_cut_contours.shift = box.center() - m_cut_contours.position; - } } else m_cut_contours.contours.reset(); From 432629ec97c522efa76a9914ffdbbb4d80c768df Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Aug 2021 09:08:39 +0200 Subject: [PATCH 143/240] Fixed compile warning --- src/slic3r/GUI/GUI_Preview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 5f9ad5ba51..8c10fb1576 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -789,8 +789,8 @@ void Preview::update_layers_slider_mode() return false; for (ModelVolume* volume : object->volumes) - if (volume->config.has("extruder") && - volume->config.option("extruder")->getInt() != extruder || + if ((volume->config.has("extruder") && + volume->config.option("extruder")->getInt() != extruder) || !volume->mmu_segmentation_facets.empty()) return false; From 5e0cbbef836746dd51fcaf4c1267bc89b778cfd9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Aug 2021 09:36:01 +0200 Subject: [PATCH 144/240] Fix SLA support strut piercing into model --- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 3c39c64e6b..6134e1f5ab 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -519,7 +519,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &hjp, auto [polar, azimuth] = dir_to_spheric(dir); polar = PI - m_cfg.bridge_slope; Vec3d d = spheric_to_dir(polar, azimuth).normalized(); - double t = bridge_mesh_distance(endp, dir, radius); + double t = bridge_mesh_distance(endp, d, radius); double tmax = std::min(m_cfg.max_bridge_length_mm, t); t = 0.; From 9767747b3b75f99081070cd1e870d4924502184f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Aug 2021 09:54:21 +0200 Subject: [PATCH 145/240] #6796 - Fixed crash when selecting Thick Bridges option and then slicing --- src/slic3r/GUI/3DScene.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 992ed7353f..8ef333d69e 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -303,13 +303,16 @@ void GLVolume::SinkingContours::render() void GLVolume::SinkingContours::update() { - if (m_parent.is_sinking() && !m_parent.is_below_printbed()) { + int object_idx = m_parent.object_idx(); + Model& model = GUI::wxGetApp().plater()->model(); + + if (0 <= object_idx && object_idx < model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { m_old_box = box; m_shift = Vec3d::Zero(); - const TriangleMesh& mesh = GUI::wxGetApp().plater()->model().objects[m_parent.object_idx()]->volumes[m_parent.volume_idx()]->mesh(); + const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh(); assert(mesh.has_shared_vertices()); m_model.reset(); From ad0902e44e74727936dbc3eb057fb8b2bf4b3ed7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Aug 2021 10:05:12 +0200 Subject: [PATCH 146/240] Update build tutorial to reflect current state Reacting to suggestion in #6615 --- doc/How to build - Linux et al.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/How to build - Linux et al.md b/doc/How to build - Linux et al.md index b23fef7728..cf47c93921 100644 --- a/doc/How to build - Linux et al.md +++ b/doc/How to build - Linux et al.md @@ -2,10 +2,10 @@ # Building PrusaSlicer on UNIX/Linux PrusaSlicer uses the CMake build system and requires several dependencies. -The dependencies can be listed in `deps/deps-linux.cmake` and `deps/deps-unix-common.cmake`, although they don't necessarily need to be as recent -as the versions listed - generally versions available on conservative Linux distros such as Debian stable or CentOS should suffice. +The dependencies can be listed in the `deps` directory in individual subdirectories, although they don't necessarily need to be as recent +as the versions listed - generally versions available on conservative Linux distros such as Debian stable, Ubuntu LTS releases or Fedora are likely sufficient. -Perl is not required any more. +Perl is not required anymore. In a typical situation, one would open a command line, go to the PrusaSlicer sources (**the root directory of the repository**), create a directory called `build` or similar, `cd` into it and call: From 740067c576a2f4c8c6eb7097c51c85fa1c3ff38d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Aug 2021 10:23:18 +0200 Subject: [PATCH 147/240] Fixed cut contours in gizmo cut after deleting an object --- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 14 ++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 10 ++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 8ef333d69e..9c0341ff45 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -306,7 +306,7 @@ void GLVolume::SinkingContours::update() int object_idx = m_parent.object_idx(); Model& model = GUI::wxGetApp().plater()->model(); - if (0 <= object_idx && object_idx < model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { + if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { m_old_box = box; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7d3f5c3dfb..40654422e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -55,8 +55,15 @@ std::string GLGizmoCut::on_get_name() const void GLGizmoCut::on_set_state() { // Reset m_cut_z on gizmo activation +#if ENABLE_SINKING_CONTOURS + if (get_state() == On) { + m_cut_z = bounding_box().center().z(); + m_cut_contours.reset(); + } +#else if (get_state() == On) m_cut_z = bounding_box().center().z(); +#endif // ENABLE_SINKING_CONTOURS } bool GLGizmoCut::on_is_activable() const @@ -211,10 +218,9 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) if (cut_clicked && (m_keep_upper || m_keep_lower)) { perform_cut(m_parent.get_selection()); - m_cut_contours.cut_z = 0.0f; - m_cut_contours.object_idx = -1; - m_cut_contours.instance_idx = -1; - m_cut_contours.contours.reset(); +#if ENABLE_SINKING_CONTOURS + m_cut_contours.reset(); +#endif // ENABLE_SINKING_CONTOURS } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index abd8793503..ffea14ad44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -35,6 +35,16 @@ class GLGizmoCut : public GLGizmoBase Vec3d shift{ Vec3d::Zero() }; int object_idx{ -1 }; int instance_idx{ -1 }; + + void reset() { + mesh.clear(); + contours.reset(); + cut_z = 0.0; + position = Vec3d::Zero(); + shift = Vec3d::Zero(); + object_idx = -1; + instance_idx = -1; + } }; CutContours m_cut_contours; From 1e863cc03181aeff0989c3c9974d0850a0ad7600 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 18 Aug 2021 10:37:08 +0200 Subject: [PATCH 148/240] Add restriction for simplification Refuse outgoing during simlification. Refuse start simplification when other Gizmo is active Fix close after preview to revert changes Allow change model for simplification --- src/slic3r/GUI/GUI_ObjectList.cpp | 18 ++++++++- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 47 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 8 +++- src/slic3r/GUI/Plater.cpp | 6 +++ 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 07119b8de1..decd55dd2a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3961,7 +3961,23 @@ void ObjectList::fix_through_netfabb() void ObjectList::simplify() { - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + auto plater = wxGetApp().plater(); + GLGizmosManager& gizmos_mgr = plater->canvas3D()->get_gizmos_manager(); + + // Do not simplify when a gizmo is open. There might be issues with updates + // and what is worse, the snapshot time would refer to the internal stack. + auto current_type = gizmos_mgr.get_current_type(); + if (current_type == GLGizmosManager::Simplify) { + // close first + gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); + }else if (current_type != GLGizmosManager::Undefined) { + plater->get_notification_manager()->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Please close all manipulators available from " + "the left toolbar before start simplify the mesh.")); + return; + } gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 06a999cad6..a921a121e2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -1,17 +1,14 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoSimplify.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/NotificationManager.hpp" +#include "slic3r/GUI/Plater.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/QuadricEdgeCollapse.hpp" -#include -#include - namespace Slic3r::GUI { GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, @@ -38,7 +35,6 @@ bool GLGizmoSimplify::on_init() return true; } - std::string GLGizmoSimplify::on_get_name() const { return (_L("Simplify")).ToUTF8().data(); @@ -195,6 +191,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi process(); } else { // use preview and close + if (m_original_its.has_value()) { + // fix hollowing, sla support points, modifiers, ... + auto plater = wxGetApp().plater(); + plater->changed_mesh(m_obj_index); + } close(); } } @@ -213,18 +214,17 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_need_reload) { m_need_reload = false; - + bool close_on_end = (m_state == State::close_on_end); // Reload visualization of mesh - change VBO, FBO on GPU m_parent.reload_scene(true); - if (m_state == State::close_on_end) { + // set m_state must be before close() !!! + m_state = State::settings; + if (close_on_end) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); plater->changed_mesh(m_obj_index); close(); } - - // change from simplifying | apply - m_state = State::settings; // Fix warning icon in object list wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1); @@ -287,7 +287,6 @@ void GLGizmoSimplify::process() m_last_error = max_error; } catch (SimplifyCanceledException &) { // set state out of main thread - m_last_error = {}; m_state = State::settings; } // need to render last status fn to change bar graph to buttons @@ -310,10 +309,30 @@ bool GLGizmoSimplify::on_is_activable() const } void GLGizmoSimplify::on_set_state() -{ +{ // Closing gizmo. e.g. selecting another one if (GLGizmoBase::m_state == GLGizmoBase::Off) { - m_volume = nullptr; + + // refuse outgoing during simlification + if (m_state != State::settings) { + GLGizmoBase::m_state = GLGizmoBase::On; + auto notification_manager = wxGetApp().plater()->get_notification_manager(); + notification_manager->push_notification( + NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Wait until Simplification ends or Cancel process.")); + return; + } + + // revert preview + if (m_original_its.has_value()) { + set_its(*m_original_its); + m_parent.reload_scene(true); + m_need_reload = false; + } + + // invalidate selected model + m_volume = nullptr; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 5a84cabad2..4539fef018 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -1,14 +1,20 @@ #ifndef slic3r_GLGizmoSimplify_hpp_ #define slic3r_GLGizmoSimplify_hpp_ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, +// which overrides our localization "L" macro. #include "GLGizmoBase.hpp" -#include "libslic3r/Model.hpp" +#include "admesh/stl.h" // indexed_triangle_set #include #include namespace Slic3r { + +class ModelVolume; + namespace GUI { + class GLGizmoSimplify : public GLGizmoBase { public: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22a1b36ae0..e037a0ac88 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4283,6 +4283,12 @@ bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_simplify() const { + // is object for simplification selected + if (get_selected_object_idx() < 0) return false; + // is already opened? + if (q->canvas3D()->get_gizmos_manager().get_current_type() == + GLGizmosManager::EType::Simplify) + return false; return true; } From 3cf6d1c8889689b8662ba77aab4f913cba8b876b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 18 Aug 2021 11:17:41 +0200 Subject: [PATCH 149/240] Revert 740067c and e645f4a 740067c: "Fixed cut contours in gizmo cut after deleting an object" e645f4a: "Fixed cut contours after cutting an object in gizmo cut" --- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 16 +++------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 10 ---------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 9c0341ff45..8ef333d69e 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -306,7 +306,7 @@ void GLVolume::SinkingContours::update() int object_idx = m_parent.object_idx(); Model& model = GUI::wxGetApp().plater()->model(); - if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { + if (0 <= object_idx && object_idx < model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { m_old_box = box; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 40654422e8..707726e080 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -55,15 +55,8 @@ std::string GLGizmoCut::on_get_name() const void GLGizmoCut::on_set_state() { // Reset m_cut_z on gizmo activation -#if ENABLE_SINKING_CONTOURS - if (get_state() == On) { - m_cut_z = bounding_box().center().z(); - m_cut_contours.reset(); - } -#else if (get_state() == On) m_cut_z = bounding_box().center().z(); -#endif // ENABLE_SINKING_CONTOURS } bool GLGizmoCut::on_is_activable() const @@ -216,12 +209,8 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) m_imgui->end(); - if (cut_clicked && (m_keep_upper || m_keep_lower)) { + if (cut_clicked && (m_keep_upper || m_keep_lower)) perform_cut(m_parent.get_selection()); -#if ENABLE_SINKING_CONTOURS - m_cut_contours.reset(); -#endif // ENABLE_SINKING_CONTOURS - } } void GLGizmoCut::set_cut_z(double cut_z) @@ -319,8 +308,9 @@ void GLGizmoCut::update_contours() m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); } } - else if (box.center() != m_cut_contours.position) + else if (box.center() != m_cut_contours.position) { m_cut_contours.shift = box.center() - m_cut_contours.position; + } } else m_cut_contours.contours.reset(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ffea14ad44..abd8793503 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -35,16 +35,6 @@ class GLGizmoCut : public GLGizmoBase Vec3d shift{ Vec3d::Zero() }; int object_idx{ -1 }; int instance_idx{ -1 }; - - void reset() { - mesh.clear(); - contours.reset(); - cut_z = 0.0; - position = Vec3d::Zero(); - shift = Vec3d::Zero(); - object_idx = -1; - instance_idx = -1; - } }; CutContours m_cut_contours; From 8a5278354efb2062d9b5c002193f50b437c4429c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 18 Aug 2021 11:14:31 +0200 Subject: [PATCH 150/240] Fixed updating of cut contour in cut gizmo by comparing ObjectIDs, for consistency with other gizmos and hopefully more robustness --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++++----- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 707726e080..d7bdf94742 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -282,21 +282,21 @@ void GLGizmoCut::update_contours() const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); - const int object_idx = selection.get_object_idx(); + const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; const int instance_idx = selection.get_instance_idx(); if (0.0 < m_cut_z && m_cut_z < m_max_z) { - if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_idx != object_idx || m_cut_contours.instance_idx != instance_idx) { + if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || m_cut_contours.instance_idx != instance_idx) { m_cut_contours.cut_z = m_cut_z; - if (m_cut_contours.object_idx != object_idx) { - m_cut_contours.mesh = wxGetApp().plater()->model().objects[object_idx]->raw_mesh(); + if (m_cut_contours.object_id != model_object->id()) { + m_cut_contours.mesh = model_object->raw_mesh(); m_cut_contours.mesh.repair(); } m_cut_contours.position = box.center(); m_cut_contours.shift = Vec3d::Zero(); - m_cut_contours.object_idx = object_idx; + m_cut_contours.object_id = model_object->id(); m_cut_contours.instance_idx = instance_idx; m_cut_contours.contours.reset(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index abd8793503..b691357afa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -5,6 +5,7 @@ #if ENABLE_SINKING_CONTOURS #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/ObjectID.hpp" #endif // ENABLE_SINKING_CONTOURS namespace Slic3r { @@ -33,7 +34,7 @@ class GLGizmoCut : public GLGizmoBase double cut_z{ 0.0 }; Vec3d position{ Vec3d::Zero() }; Vec3d shift{ Vec3d::Zero() }; - int object_idx{ -1 }; + ObjectID object_id; int instance_idx{ -1 }; }; From 27fcf55eaa61dd9b7fa793801099ce110e7f08f0 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 18 Aug 2021 12:07:46 +0200 Subject: [PATCH 151/240] Add cancel and statusFn into init phase of simplification Move debug functions into NDEBUG macro --- src/libslic3r/QuadricEdgeCollapse.cpp | 293 +++++++++++++++----------- 1 file changed, 171 insertions(+), 122 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index b5568a171e..c4e874c65e 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -7,13 +7,14 @@ using namespace Slic3r; -// only private namespace not neccessary be in hpp +// only private namespace not neccessary be in .hpp namespace QuadricEdgeCollapse { using Vertices = std::vector; using Triangle = stl_triangle_vertex_indices; using Indices = std::vector; using SymMat = SimplifyMesh::implementation::SymetricMatrix; - + using ThrowOnCancel = std::function; + using StatusFn = std::function; // smallest error caused by edges, identify smallest edge in triangle struct Error { @@ -74,7 +75,8 @@ namespace QuadricEdgeCollapse { // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number double vertex_error(const SymMat &q, const Vec3d &vertex); SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices); - std::tuple init(const indexed_triangle_set &its); + std::tuple + init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn); std::optional find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, @@ -89,129 +91,24 @@ namespace QuadricEdgeCollapse { uint32_t vi0, uint32_t vi1, uint32_t vi_top0, const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1); void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its); + +#ifndef NDEBUG + void store_surround(const char *obj_filename, size_t triangle_index, int depth, const indexed_triangle_set &its, + const VertexInfos &v_infos, const EdgeInfos &e_infos); + bool check_neighbors(const indexed_triangle_set &its, const TriangleInfos &t_infos, + const VertexInfos &v_infos, const EdgeInfos &e_infos); +#endif /* NDEBUG */ + } // namespace QuadricEdgeCollapse using namespace QuadricEdgeCollapse; -// store triangle surrounding to file -void store_surround(const char * obj_filename, - size_t triangle_index, - int depth, - const indexed_triangle_set &its, - const VertexInfos & v_infos, - const EdgeInfos & e_infos) -{ - std::set triangles; - // triangle index, depth - using Item = std::pair; - std::queue process; - process.push({triangle_index, depth}); - - while (!process.empty()) { - Item item = process.front(); - process.pop(); - size_t ti = item.first; - auto it = triangles.find(ti); - if (it != triangles.end()) continue; - triangles.insert(ti); - if (item.second == 0) continue; - - const Vec3i &t = its.indices[ti]; - for (size_t i = 0; i < 3; ++i) { - const auto &v_info = v_infos[t[i]]; - for (size_t d = 0; d < v_info.count; ++d) { - size_t ei = v_info.start + d; - const auto & e_info = e_infos[ei]; - auto it = triangles.find(e_info.t_index); - if (it != triangles.end()) continue; - process.push({e_info.t_index, item.second - 1}); - } - } - } - - std::vector trs; - trs.reserve(triangles.size()); - for (size_t ti : triangles) trs.push_back(ti); - its_store_triangles(its, obj_filename, trs); - //its_write_obj(its,"original.obj"); -} - -bool check_neighbors(const indexed_triangle_set &its, - const TriangleInfos & t_infos, - const VertexInfos & v_infos, - const EdgeInfos & e_infos) -{ - VertexInfos v_infos2(v_infos.size()); - size_t count_indices = 0; - - for (size_t ti = 0; ti < its.indices.size(); ti++) { - if (t_infos[ti].is_deleted()) continue; - ++count_indices; - const Triangle &t = its.indices[ti]; - for (size_t e = 0; e < 3; e++) { - VertexInfo &v_info = v_infos2[t[e]]; - ++v_info.count; // triangle count - } - } - - uint32_t triangle_start = 0; - for (VertexInfo &v_info : v_infos2) { - v_info.start = triangle_start; - triangle_start += v_info.count; - // set filled vertex to zero - v_info.count = 0; - } - - // create reference - EdgeInfos e_infos2(count_indices * 3); - for (size_t ti = 0; ti < its.indices.size(); ti++) { - if (t_infos[ti].is_deleted()) continue; - const Triangle &t = its.indices[ti]; - for (size_t j = 0; j < 3; ++j) { - VertexInfo &v_info = v_infos2[t[j]]; - size_t ei = v_info.start + v_info.count; - assert(ei < e_infos2.size()); - EdgeInfo &e_info = e_infos2[ei]; - e_info.t_index = ti; - e_info.edge = j; - ++v_info.count; - } - } - - for (size_t vi = 0; vi < its.vertices.size(); vi++) { - const VertexInfo &v_info = v_infos[vi]; - if (v_info.is_deleted()) continue; - const VertexInfo &v_info2 = v_infos2[vi]; - if (v_info.count != v_info2.count) { - return false; - } - EdgeInfos eis; - eis.reserve(v_info.count); - std::copy(e_infos.begin() + v_info.start, - e_infos.begin() + v_info.start + v_info.count, - std::back_inserter(eis)); - auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { - return ei1.t_index < ei2.t_index; - }; - std::sort(eis.begin(), eis.end(), compare); - std::sort(e_infos2.begin() + v_info2.start, - e_infos2.begin() + v_info2.start + v_info2.count, - compare); - for (size_t ei = 0; ei < v_info.count; ++ei) { - if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { - return false; - } - } - } - return true; -} - void Slic3r::its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count, float * max_error, std::function throw_on_cancel, - std::function statusfn) + std::function status_fn) { // constants --> may be move to config const int status_init_size = 10; // in percents @@ -222,15 +119,19 @@ void Slic3r::its_quadric_edge_collapse( float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error; if (maximal_error <= 0.f) return; if (throw_on_cancel == nullptr) throw_on_cancel = []() {}; - if (statusfn == nullptr) statusfn = [](int) {}; + if (status_fn == nullptr) status_fn = [](int) {}; + + StatusFn init_status_fn = [&](int percent) { + status_fn(std::round((percent * status_init_size) / 100.)); + }; TriangleInfos t_infos; // only normals with information about deleted triangle VertexInfos v_infos; EdgeInfos e_infos; Errors errors; - std::tie(t_infos, v_infos, e_infos, errors) = init(its); + std::tie(t_infos, v_infos, e_infos, errors) = init(its, throw_on_cancel, init_status_fn); throw_on_cancel(); - statusfn(status_init_size); + status_fn(status_init_size); //its_store_triangle(its, "triangle.obj", 1182); //store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos); @@ -259,7 +160,7 @@ void Slic3r::its_quadric_edge_collapse( (double) count_triangle_to_reduce; double status = status_init_size + (100 - status_init_size) * (1. - reduced); - statusfn(static_cast(std::round(status))); + status_fn(static_cast(std::round(status))); }; // modulo for update status uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100); @@ -481,8 +382,16 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t, } std::tuple -QuadricEdgeCollapse::init(const indexed_triangle_set &its) +QuadricEdgeCollapse::init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn) { + // change speed of progress bargraph + const int status_normal_size = 25; + const int status_sum_quadric = 25; + const int status_set_offsets = 10; + const int status_calc_errors = 30; + const int status_create_refs = 10; + + int status_offset = 0; TriangleInfos t_infos(its.indices.size()); VertexInfos v_infos(its.vertices.size()); { @@ -496,8 +405,13 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) Vec3d normal = create_normal(t, its.vertices); t_info.n = normal.cast(); triangle_quadrics[i] = create_quadric(t, normal, its.vertices); + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_normal_size) / its.indices.size()); + } } }); // END parallel for + status_offset += status_normal_size; // sum quadrics for (size_t i = 0; i < its.indices.size(); i++) { @@ -508,7 +422,12 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) v_info.q += q; ++v_info.count; // triangle count } + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_sum_quadric) / its.indices.size()); + } } + status_offset += status_sum_quadric; } // remove triangle quadrics // set offseted starts @@ -521,6 +440,10 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) } assert(its.indices.size() * 3 == triangle_start); + status_offset += status_set_offsets; + throw_on_cancel(); + status_fn(status_offset); + // calc error Errors errors(its.indices.size()); tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), @@ -529,8 +452,15 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) const Triangle &t = its.indices[i]; TriangleInfo & t_info = t_infos[i]; errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index); + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_calc_errors) / its.indices.size()); + } + if (i % 1000000 == 0) throw_on_cancel(); } }); // END parallel for + + status_offset += status_calc_errors; // create reference EdgeInfos e_infos(its.indices.size() * 3); @@ -545,7 +475,14 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) e_info.edge = j; ++v_info.count; } + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_create_refs) / its.indices.size()); + } } + + throw_on_cancel(); + status_fn(100); return {t_infos, v_infos, e_infos, errors}; } @@ -794,3 +731,115 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, } its.indices.erase(its.indices.begin() + ti_new, its.indices.end()); } + +#ifndef NDEBUG +// store triangle surrounding to file +void QuadricEdgeCollapse::store_surround(const char *obj_filename, + size_t triangle_index, + int depth, + const indexed_triangle_set &its, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + std::set triangles; + // triangle index, depth + using Item = std::pair; + std::queue process; + process.push({triangle_index, depth}); + + while (!process.empty()) { + Item item = process.front(); + process.pop(); + size_t ti = item.first; + auto it = triangles.find(ti); + if (it != triangles.end()) continue; + triangles.insert(ti); + if (item.second == 0) continue; + + const Vec3i &t = its.indices[ti]; + for (size_t i = 0; i < 3; ++i) { + const auto &v_info = v_infos[t[i]]; + for (size_t d = 0; d < v_info.count; ++d) { + size_t ei = v_info.start + d; + const auto &e_info = e_infos[ei]; + auto it = triangles.find(e_info.t_index); + if (it != triangles.end()) continue; + process.push({e_info.t_index, item.second - 1}); + } + } + } + + std::vector trs; + trs.reserve(triangles.size()); + for (size_t ti : triangles) trs.push_back(ti); + its_store_triangles(its, obj_filename, trs); + // its_write_obj(its,"original.obj"); +} + +bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its, + const TriangleInfos & t_infos, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + VertexInfos v_infos2(v_infos.size()); + size_t count_indices = 0; + + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + ++count_indices; + const Triangle &t = its.indices[ti]; + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos2[t[e]]; + ++v_info.count; // triangle count + } + } + + uint32_t triangle_start = 0; + for (VertexInfo &v_info : v_infos2) { + v_info.start = triangle_start; + triangle_start += v_info.count; + // set filled vertex to zero + v_info.count = 0; + } + + // create reference + EdgeInfos e_infos2(count_indices * 3); + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + const Triangle &t = its.indices[ti]; + for (size_t j = 0; j < 3; ++j) { + VertexInfo &v_info = v_infos2[t[j]]; + size_t ei = v_info.start + v_info.count; + assert(ei < e_infos2.size()); + EdgeInfo &e_info = e_infos2[ei]; + e_info.t_index = ti; + e_info.edge = j; + ++v_info.count; + } + } + + for (size_t vi = 0; vi < its.vertices.size(); vi++) { + const VertexInfo &v_info = v_infos[vi]; + if (v_info.is_deleted()) continue; + const VertexInfo &v_info2 = v_infos2[vi]; + if (v_info.count != v_info2.count) { return false; } + EdgeInfos eis; + eis.reserve(v_info.count); + std::copy(e_infos.begin() + v_info.start, + e_infos.begin() + v_info.start + v_info.count, + std::back_inserter(eis)); + auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { + return ei1.t_index < ei2.t_index; + }; + std::sort(eis.begin(), eis.end(), compare); + std::sort(e_infos2.begin() + v_info2.start, + e_infos2.begin() + v_info2.start + v_info2.count, compare); + for (size_t ei = 0; ei < v_info.count; ++ei) { + if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { + return false; + } + } + } + return true; +} +#endif /* NDEBUG */ From 120be3a76d6a85d3598a366226667106085e5202 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 18 Aug 2021 13:04:45 +0200 Subject: [PATCH 152/240] Fixed compile warning --- src/slic3r/GUI/3DScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 8ef333d69e..9c0341ff45 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -306,7 +306,7 @@ void GLVolume::SinkingContours::update() int object_idx = m_parent.object_idx(); Model& model = GUI::wxGetApp().plater()->model(); - if (0 <= object_idx && object_idx < model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { + if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) { const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { m_old_box = box; From a3f995ac45e92f0987a5c7714316f1dbc1034bbb Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 18 Aug 2021 15:02:36 +0200 Subject: [PATCH 153/240] Add "Revert" buttons for text colors Settings --- src/slic3r/GUI/ButtonsDescription.cpp | 81 ++++++++++++++++----------- src/slic3r/GUI/ButtonsDescription.hpp | 4 ++ src/slic3r/GUI/GUI_App.cpp | 21 ++++--- src/slic3r/GUI/GUI_App.hpp | 2 + src/slic3r/GUI/Preferences.cpp | 31 ++-------- 5 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp index cadb7ac24c..2f8c2b6817 100644 --- a/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -12,6 +12,46 @@ namespace Slic3r { namespace GUI { +void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour) +{ + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5); + sizer->Add(grid_sizer, 0, wxEXPAND); + + ScalableBitmap bmp_delete = ScalableBitmap(parent, "cross"); + ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus"); + + auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) { + // + auto sys_label = new wxStaticText(parent, wxID_ANY, label_text); + sys_label->SetForegroundColour(color); + + *color_picker = new wxColourPickerCtrl(parent, wxID_ANY, color); + wxGetApp().UpdateDarkUI((*color_picker)->GetPickerCtrl(), true); + (*color_picker)->Bind(wxEVT_COLOURPICKER_CHANGED, [color_picker, sys_label](wxCommandEvent&) { + sys_label->SetForegroundColour((*color_picker)->GetColour()); + sys_label->Refresh(); + }); + + auto btn = new ScalableButton(parent, wxID_ANY, "undo"); + btn->SetToolTip(_L("Revert color to default")); + btn->Bind(wxEVT_BUTTON, [sys_label, color_picker, def_color](wxEvent& event) { + (*color_picker)->SetColour(def_color); + sys_label->SetForegroundColour(def_color); + sys_label->Refresh(); + }); + parent->Bind(wxEVT_UPDATE_UI, [color_picker, def_color](wxUpdateUIEvent& evt) { + evt.Enable((*color_picker)->GetColour() != def_color); + }, btn->GetId()); + + grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL); + grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + }; + + add_color(sys_colour, wxGetApp().get_label_clr_sys(), wxGetApp().get_label_default_clr_system(), _L("Value is the same as the system value")); + add_color(mod_colour, wxGetApp().get_label_clr_modified(),wxGetApp().get_label_default_clr_modified(), _L("Value was changed and is not equal to the system value or the last saved preset")); +} + ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector &entries) : wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize), m_entries(entries) @@ -35,50 +75,23 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vectorSetForegroundColour(wxGetApp().get_label_clr_sys()); - auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_sys()); - wxGetApp().UpdateDarkUI(sys_colour->GetPickerCtrl(), true); - sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e) - { - sys_label->SetForegroundColour(sys_colour->GetColour()); - sys_label->Refresh(); - })); - size_t t= 0; - while (t < 3) { - grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - ++t; - } - grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(sys_colour, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - - auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset"))); - mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified()); - auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_modified()); - wxGetApp().UpdateDarkUI(mod_colour->GetPickerCtrl(), true); - mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e) - { - mod_label->SetForegroundColour(mod_colour->GetColour()); - mod_label->Refresh(); - })); - grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + FillSizerWithTextColorDescriptions(sizer, this, &sys_colour, &mod_colour); + main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20); auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL); main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - wxGetApp().UpdateDlgDarkUI(this, true); - wxButton* btn = static_cast(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) { + btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { wxGetApp().set_label_clr_sys(sys_colour->GetColour()); wxGetApp().set_label_clr_modified(mod_colour->GetColour()); EndModal(wxID_OK); }); + wxGetApp().UpdateDarkUI(btn); + wxGetApp().UpdateDarkUI(static_cast(FindWindowById(wxID_CANCEL, this))); + SetSizer(main_sizer); main_sizer->SetSizeHints(this); } diff --git a/src/slic3r/GUI/ButtonsDescription.hpp b/src/slic3r/GUI/ButtonsDescription.hpp index 5ca74290ad..921ce796ef 100644 --- a/src/slic3r/GUI/ButtonsDescription.hpp +++ b/src/slic3r/GUI/ButtonsDescription.hpp @@ -11,6 +11,8 @@ namespace GUI { class ButtonsDescription : public wxDialog { + wxColourPickerCtrl* sys_colour{ nullptr }; + wxColourPickerCtrl* mod_colour{ nullptr }; public: struct Entry { Entry(ScalableBitmap *bitmap, const std::string &symbol, const std::string &explanation) : bitmap(bitmap), symbol(symbol), explanation(explanation) {} @@ -23,6 +25,8 @@ public: ButtonsDescription(wxWindow* parent, const std::vector &entries); ~ButtonsDescription() {} + static void FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour); + private: std::vector m_entries; }; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index ec1dce508f..91d16c602b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1030,17 +1030,22 @@ bool GUI_App::dark_mode() #endif } +const wxColour GUI_App::get_label_default_clr_system() +{ + return dark_mode() ? wxColour(115, 220, 103) : wxColour(26, 132, 57); +} + +const wxColour GUI_App::get_label_default_clr_modified() +{ + return dark_mode() ? wxColour(253, 111, 40) : wxColour(252, 77, 1); +} + void GUI_App::init_label_colours() { + m_color_label_modified = get_label_default_clr_modified(); + m_color_label_sys = get_label_default_clr_system(); + bool is_dark_mode = dark_mode(); - if (is_dark_mode) { - m_color_label_modified = wxColour(253, 111, 40); - m_color_label_sys = wxColour(115, 220, 103); - } - else { - m_color_label_modified = wxColour(252, 77, 1); - m_color_label_sys = wxColour(26, 132, 57); - } #ifdef _WIN32 m_color_label_default = is_dark_mode ? wxColour(250, 250, 250): wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); m_color_highlight_label_default = is_dark_mode ? wxColour(230, 230, 230): wxSystemSettings::GetColour(/*wxSYS_COLOUR_HIGHLIGHTTEXT*/wxSYS_COLOUR_WINDOWTEXT); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index f6699e82b4..a140247e09 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -179,6 +179,8 @@ public: static unsigned get_colour_approx_luma(const wxColour &colour); static bool dark_mode(); + const wxColour get_label_default_clr_system(); + const wxColour get_label_default_clr_modified(); void init_label_colours(); void update_label_colours_from_appconfig(); void update_label_colours(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 50a5993a9b..53c93b6b02 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -7,6 +7,7 @@ #include "libslic3r/AppConfig.hpp" #include #include "Notebook.hpp" +#include "ButtonsDescription.hpp" namespace Slic3r { namespace GUI { @@ -395,7 +396,8 @@ void PreferencesDialog::build(size_t selected_tab) auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); this->Bind(wxEVT_BUTTON, &PreferencesDialog::accept, this, wxID_OK); - wxGetApp().UpdateDlgDarkUI(this, true); + for (int id : {wxID_OK, wxID_CANCEL}) + wxGetApp().UpdateDarkUI(static_cast(FindWindowById(id, this))); sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10); @@ -638,32 +640,7 @@ void PreferencesDialog::create_settings_text_color_widget() if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); wxSizer* sizer = new wxStaticBoxSizer(stb, wxVERTICAL); - wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 5, 5); - sizer->Add(grid_sizer, 0, wxEXPAND); - - auto sys_label = new wxStaticText(parent, wxID_ANY, _L("Value is the same as the system value")); - sys_label->SetForegroundColour(wxGetApp().get_label_clr_sys()); - m_sys_colour = new wxColourPickerCtrl(parent, wxID_ANY, wxGetApp().get_label_clr_sys()); - wxGetApp().UpdateDarkUI(m_sys_colour->GetPickerCtrl(), true); - m_sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, [this, sys_label](wxCommandEvent&) { - sys_label->SetForegroundColour(m_sys_colour->GetColour()); - sys_label->Refresh(); - }); - - grid_sizer->Add(m_sys_colour, 0, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - - auto mod_label = new wxStaticText(parent, wxID_ANY, _L("Value was changed and is not equal to the system value or the last saved preset")); - mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified()); - m_mod_colour = new wxColourPickerCtrl(parent, wxID_ANY, wxGetApp().get_label_clr_modified()); - wxGetApp().UpdateDarkUI(m_mod_colour->GetPickerCtrl(), true); - m_mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, [this, mod_label](wxCommandEvent&) { - mod_label->SetForegroundColour(m_mod_colour->GetColour()); - mod_label->Refresh(); - }); - - grid_sizer->Add(m_mod_colour, 0, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(mod_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + ButtonsDescription::FillSizerWithTextColorDescriptions(sizer, parent, &m_sys_colour, &m_mod_colour); m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit()); } From 5be61fb31c4985a9aafddc3b6f4202375d2d5b19 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 18 Aug 2021 15:29:51 +0200 Subject: [PATCH 154/240] Fix build on Linux (gcc 8.4) --- src/slic3r/GUI/ButtonsDescription.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/ButtonsDescription.hpp b/src/slic3r/GUI/ButtonsDescription.hpp index 921ce796ef..fbed36c2ab 100644 --- a/src/slic3r/GUI/ButtonsDescription.hpp +++ b/src/slic3r/GUI/ButtonsDescription.hpp @@ -5,6 +5,7 @@ #include class ScalableBitmap; +class wxColourPickerCtrl; namespace Slic3r { namespace GUI { From caf8ef4aab73edace0a2d3a3f71a3f277d769f67 Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko Date: Wed, 18 Aug 2021 16:10:35 +0200 Subject: [PATCH 155/240] Auto selection of presets (#6817) PresetComboBoxes: Auto selection of printer/material preset from the new added presets in ConfigWizard + Check unsaved changes if ConfigWizard is running from the PesetComboBoxes --- src/libslic3r/Preset.cpp | 8 +- src/libslic3r/Preset.hpp | 2 +- src/libslic3r/PresetBundle.cpp | 20 +++-- src/libslic3r/PresetBundle.hpp | 13 ++- src/slic3r/GUI/ConfigWizard.cpp | 128 ++++++++++++++++++---------- src/slic3r/GUI/PresetComboBoxes.cpp | 14 ++- 6 files changed, 127 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 1283d0ac1a..61ae78b5eb 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1385,12 +1385,16 @@ const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConf return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1); } -const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model_id) const +const Preset* PrinterPresetCollection::find_system_preset_by_model_and_variant(const std::string &model_id, const std::string& variant) const { if (model_id.empty()) { return nullptr; } const auto it = std::find_if(cbegin(), cend(), [&](const Preset &preset) { - return preset.config.opt_string("printer_model") == model_id; + if (!preset.is_system || preset.config.opt_string("printer_model") != model_id) + return false; + if (variant.empty()) + return true; + return preset.config.opt_string("printer_variant") == variant; }); return it != cend() ? &*it : nullptr; diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 9d5597cac1..4897f504c7 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -592,7 +592,7 @@ public: const Preset& default_preset_for(const DynamicPrintConfig &config) const override; - const Preset* find_by_model_id(const std::string &model_id) const; + const Preset* find_system_preset_by_model_and_variant(const std::string &model_id, const std::string &variant) const; private: PrinterPresetCollection() = default; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index adb84df67c..95d93b1c2a 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -188,7 +188,8 @@ void PresetBundle::setup_directories() } } -PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, const std::string &preferred_model_id) +PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, + const PresetPreferences& preferred_selection/* = PresetPreferences()*/) { // First load the vendor specific system presets. PresetsConfigSubstitutions substitutions; @@ -239,7 +240,8 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward if (! errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative); - this->load_selections(config, preferred_model_id); + // ysToDo : set prefered filament or sla_material (relates to print technology) and force o use of preffered printer model if it was added + this->load_selections(config, preferred_selection); return substitutions; } @@ -441,7 +443,7 @@ void PresetBundle::load_installed_sla_materials(AppConfig &config) // Load selections (current print, current filaments, current printer) from config.ini // This is done on application start up or after updates are applied. -void PresetBundle::load_selections(AppConfig &config, const std::string &preferred_model_id) +void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& preferred_selection/* = PresetPreferences()*/) { // Update visibility of presets based on application vendor / model / variant configuration. this->load_installed_printers(config); @@ -464,13 +466,21 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr // will be selected by the following call of this->update_compatible(PresetSelectCompatibleType::Always). const Preset *initial_printer = printers.find_preset(initial_printer_profile_name); - const Preset *preferred_printer = printers.find_by_model_id(preferred_model_id); + const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant); printers.select_preset_by_name( - (preferred_printer != nullptr && (initial_printer == nullptr || !initial_printer->is_visible)) ? + (preferred_printer != nullptr /*&& (initial_printer == nullptr || !initial_printer->is_visible)*/) ? preferred_printer->name : initial_printer_profile_name, true); + // select preferred filament/sla_material profile if any exists and is visible + if (!preferred_selection.filament.empty()) + if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible) + initial_filament_profile_name = it->name; + if (!preferred_selection.sla_material.empty()) + if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible) + initial_sla_material_profile_name = it->name; + // Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found. prints.select_preset_by_name_strict(initial_print_profile_name); filaments.select_preset_by_name_strict(initial_filament_profile_name); diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 3c7349668a..c22599e388 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -25,9 +25,18 @@ public: void setup_directories(); + struct PresetPreferences { + std::string printer_model_id;// name of a preferred printer model + std::string printer_variant; // name of a preferred printer variant + std::string filament; // name of a preferred filament preset + std::string sla_material; // name of a preferred sla_material preset + }; + // Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets. // Load selections (current print, current filaments, current printer) from config.ini - PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule, const std::string &preferred_model_id = std::string()); + // select preferred presets, if any exist + PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule, + const PresetPreferences& preferred_selection = PresetPreferences()); // Export selections (current print, current filaments, current printer) into config.ini void export_selections(AppConfig &config); @@ -153,7 +162,7 @@ private: // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. - void load_selections(AppConfig &config, const std::string &preferred_model_id = ""); + void load_selections(AppConfig &config, const PresetPreferences& preferred_selection = PresetPreferences()); // Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path. // and the external config is just referenced, not stored into user profile directory. diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index c31bc02873..f64bdecbaa 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2453,6 +2453,34 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo return true; } +static std::set get_new_added_presets(const std::map& old_data, const std::map& new_data) +{ + auto get_aliases = [](const std::map& data) { + std::set old_aliases; + for (auto item : data) { + const std::string& name = item.first; + size_t pos = name.find("@"); + old_aliases.emplace(pos == std::string::npos ? name : name.substr(0, pos-1)); + } + return old_aliases; + }; + + std::set old_aliases = get_aliases(old_data); + std::set new_aliases = get_aliases(new_data); + std::set diff; + std::set_difference(new_aliases.begin(), new_aliases.end(), old_aliases.begin(), old_aliases.end(), std::inserter(diff, diff.begin())); + + return diff; +} + +static std::string get_first_added_preset(const std::map& old_data, const std::map& new_data) +{ + std::set diff = get_new_added_presets(old_data, new_data); + if (diff.empty()) + return std::string(); + return *diff.begin(); +} + bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { const auto enabled_vendors = appconfig_new.vendors(); @@ -2525,13 +2553,61 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese preset_bundle->reset(true); } + std::string preferred_model; + std::string preferred_variant; + const auto enabled_vendors_old = app_config->vendors(); + auto get_preferred_printer_model = [enabled_vendors, enabled_vendors_old](const std::string& bundle_name, const Bundle& bundle, std::string& variant) { + const auto config = enabled_vendors.find(bundle_name); + if (config == enabled_vendors.end()) + return std::string(); + for (const auto& model : bundle.vendor_profile->models) { + if (const auto model_it = config->second.find(model.id); + model_it != config->second.end() && model_it->second.size() > 0) { + variant = *model_it->second.begin(); + const auto config_old = enabled_vendors_old.find(bundle_name); + if (config_old == enabled_vendors_old.end()) + return model.id; + const auto model_it_old = config_old->second.find(model.id); + if (model_it_old == config_old->second.end()) + return model.id; + else if (model_it_old->second != model_it->second) { + for (const auto& var : model_it->second) + if (model_it_old->second.find(var) == model_it_old->second.end()) { + variant = var; + return model.id; + } + } + } + } + if (!variant.empty()) + variant.clear(); + return std::string(); + }; + // Prusa printers are considered first, then 3rd party. + if (preferred_model = get_preferred_printer_model("PrusaResearch", bundles.prusa_bundle(), preferred_variant); + preferred_model.empty()) { + for (const auto& bundle : bundles) { + if (bundle.second.is_prusa_bundle) { continue; } + if (preferred_model = get_preferred_printer_model(bundle.first, bundle.second, preferred_variant); + !preferred_model.empty()) + break; + } + } + + std::string first_added_filament, first_added_sla_material; + auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) { + if (appconfig_new.has_section(section_name)) { + // get first of new added preset names + const std::map& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map(); + first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name)); + app_config->set_section(section_name, appconfig_new.get_section(section_name)); + } + }; + apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament); + apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material); + app_config->set_vendors(appconfig_new); - if (appconfig_new.has_section(AppConfig::SECTION_FILAMENTS)) { - app_config->set_section(AppConfig::SECTION_FILAMENTS, appconfig_new.get_section(AppConfig::SECTION_FILAMENTS)); - } - if (appconfig_new.has_section(AppConfig::SECTION_MATERIALS)) { - app_config->set_section(AppConfig::SECTION_MATERIALS, appconfig_new.get_section(AppConfig::SECTION_MATERIALS)); - } + app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0"); app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0"); @@ -2556,44 +2632,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese page_mode->serialize_mode(app_config); - std::string preferred_model; - - // Figure out the default pre-selected printer based on the selections in the pickers. - // The default is the first selected printer model (one with at least 1 variant selected). - // The default is only applied by load_presets() if the user doesn't have a (visible) printer - // selected already. - // Prusa printers are considered first, then 3rd party. - const auto config_prusa = enabled_vendors.find("PrusaResearch"); - if (config_prusa != enabled_vendors.end()) { - for (const auto &model : bundles.prusa_bundle().vendor_profile->models) { - const auto model_it = config_prusa->second.find(model.id); - if (model_it != config_prusa->second.end() && model_it->second.size() > 0) { - preferred_model = model.id; - break; - } - } - } - if (preferred_model.empty()) { - for (const auto &bundle : bundles) { - if (bundle.second.is_prusa_bundle) { continue; } - - const auto config = enabled_vendors.find(bundle.first); - if (config == enabled_vendors.end()) { continue; } - for (const auto &model : bundle.second.vendor_profile->models) { - const auto model_it = config->second.find(model.id); - if (model_it != config->second.end() && model_it->second.size() > 0) { - preferred_model = model.id; - break; - } - } - } - } - - // Reloading the configs after some modifications were done to PrusaSlicer.ini. - // Just perform the substitutions silently, as the substitutions were already presented to the user on application start-up - // and the Wizard shall not create any new values that would require substitution. - // Throw on substitutions in system profiles, as the system profiles provided over the air should be compatible with this PrusaSlicer version. - preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, preferred_model); + preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, + {preferred_model, preferred_variant, first_added_filament, first_added_sla_material}); if (page_custom->custom_wanted()) { page_firmware->apply_custom_config(*custom_config); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index aab8c28292..b8d4ead509 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -634,6 +634,12 @@ PlaterPresetComboBox::~PlaterPresetComboBox() edit_btn->Destroy(); } +static void run_wizard(ConfigWizard::StartPage sp) +{ + if (wxGetApp().check_and_save_current_preset_changes()) + wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); +} + void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt) { auto selected_item = evt.GetSelection(); @@ -653,7 +659,7 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt) case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; default: break; } - wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + wxTheApp->CallAfter([sp]() { run_wizard(sp); }); } return; } @@ -685,7 +691,7 @@ void PlaterPresetComboBox::show_add_menu() append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "", [](wxCommandEvent&) { - wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + wxTheApp->CallAfter([]() { run_wizard(ConfigWizard::SP_PRINTERS); }); }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "", @@ -715,7 +721,7 @@ void PlaterPresetComboBox::show_edit_menu() else append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "", [](wxCommandEvent&) { - wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); }); + wxTheApp->CallAfter([]() { run_wizard(ConfigWizard::SP_PRINTERS); }); }, "edit_uni", menu, []() { return true; }, wxGetApp().plater()); append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "", @@ -918,7 +924,7 @@ void TabPresetComboBox::OnSelect(wxCommandEvent &evt) this->SetSelection(m_last_selected); if (marker == LABEL_ITEM_WIZARD_PRINTERS) wxTheApp->CallAfter([this]() { - wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); + run_wizard(ConfigWizard::SP_PRINTERS); // update combobox if its parent is a PhysicalPrinterDialog PhysicalPrinterDialog* parent = dynamic_cast(this->GetParent()); From 74edeb147b66f46b879e820e2ac2eec3b60fd4fa Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Aug 2021 15:44:48 +0200 Subject: [PATCH 156/240] Add min z height orientation search --- src/libslic3r/SLA/Rotfinder.cpp | 88 ++++++++++++++++++++++++++ src/libslic3r/SLA/Rotfinder.hpp | 3 +- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 6 +- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index d18d2fe6bb..804908ab8e 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -442,4 +442,92 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, return {rot[0], rot[1]}; } +inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set& its, const Transform3f &tr) +{ + if (its.vertices.empty()) + return {}; + + Vec3f bmin = tr * its.vertices.front(), bmax = tr * its.vertices.front(); + + for (const Vec3f &p : its.vertices) { + Vec3f pp = tr * p; + bmin = pp.cwiseMin(bmin); + bmax = pp.cwiseMax(bmax); + } + + return {bmin.cast(), bmax.cast()}; +} + +Vec2d find_min_z_height_rotation(const ModelObject &mo, const RotOptimizeParams ¶ms) +{ + static const unsigned MAX_TRIES = 1000; + + // return value + XYRotation rot; + + // We will use only one instance of this converted mesh to examine different + // rotations + TriangleMesh mesh = get_mesh_to_rotate(mo); + + // To keep track of the number of iterations + unsigned status = 0; + + // The maximum number of iterations + auto max_tries = unsigned(params.accuracy() * MAX_TRIES); + + auto &statuscb = params.statuscb(); + + // call status callback with zero, because we are at the start + statuscb(status); + + auto statusfn = [&statuscb, &status, &max_tries] { + // report status + statuscb(unsigned(++status * 100.0/max_tries) ); + }; + + auto stopcond = [&statuscb] { + return ! statuscb(-1); + }; + + TriangleMesh chull = mesh.convex_hull_3d(); + chull.require_shared_vertices(); + auto inputs = reserve_vector(chull.its.indices.size()); + auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) { + double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y]; + return std::abs(xdiff) < EPSILON ? ydiff < 0. : xdiff < 0.; + }; + auto eqcmp = [](const XYRotation &r1, const XYRotation &r2) { + double xdiff = r1[X] - r2[X], ydiff = r1[Y] - r2[Y]; + return std::abs(xdiff) < EPSILON && std::abs(ydiff) < EPSILON; + }; + + for (size_t fi = 0; fi < chull.its.indices.size(); ++fi) { + Facestats fc{get_triangle_vertices(chull, fi)}; + + auto q = Eigen::Quaternionf{}.FromTwoVectors(fc.normal, DOWN); + XYRotation rot = from_transform3f(Transform3f::Identity() * q); + + auto it = std::lower_bound(inputs.begin(), inputs.end(), rot, rotcmp); + + if (it == inputs.end() || !eqcmp(*it, rot)) + inputs.insert(it, rot); + } + + inputs.shrink_to_fit(); + max_tries = inputs.size(); + + // If the model can be placed on the bed directly, we only need to + // check the 3D convex hull face rotations. + + auto objfn = [&chull, &statusfn](const XYRotation &rot) { + statusfn(); + Transform3f tr = to_transform3f(rot); + return bounding_box_with_tr(chull.its, tr).size().z(); + }; + + rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); + + return {rot[0], rot[1]}; +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Rotfinder.hpp b/src/libslic3r/SLA/Rotfinder.hpp index 77a39016db..d632419fb3 100644 --- a/src/libslic3r/SLA/Rotfinder.hpp +++ b/src/libslic3r/SLA/Rotfinder.hpp @@ -63,7 +63,8 @@ Vec2d find_best_misalignment_rotation(const ModelObject &modelobj, Vec2d find_least_supports_rotation(const ModelObject &modelobj, const RotOptimizeParams & = {}); -double find_Z_fit_to_bed_rotation(const ModelObject &mo, const BoundingBox &bed); +Vec2d find_min_z_height_rotation(const ModelObject &mo, + const RotOptimizeParams ¶ms = {}); } // namespace sla } // namespace Slic3r diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index edabb7caed..d703ff0e36 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -27,9 +27,9 @@ class RotoptimizeJob : public PlaterJob "structures.\nNote that this method will try to find the best surface of the object " "for touching the print bed if no elevation is set.")}, // Just a min area bounding box that is done for all methods anyway. - {L("Smallest bounding box (Z axis only)"), - nullptr, - L("Rotate the object only in Z axis to have the smallest bounding box.")}}; + {L("Smallest Z height"), + sla::find_min_z_height_rotation, + L("Rotate the model to have least z height for faster print time.")}}; size_t m_method_id = 0; float m_accuracy = 0.75; From 1672130d456a039388516bf76544ce15b35db311 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Aug 2021 16:47:59 +0200 Subject: [PATCH 157/240] Remove code duplication, clarify naming of orientation searches --- src/libslic3r/SLA/Rotfinder.cpp | 215 ++++++++++--------------- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 4 +- 2 files changed, 84 insertions(+), 135 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 804908ab8e..ac336731af 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -283,64 +283,61 @@ std::array find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn) } // namespace -// Assemble the mesh with the correct transformation to be used in rotation -// optimization. -TriangleMesh get_mesh_to_rotate(const ModelObject &mo) -{ - TriangleMesh mesh = mo.raw_mesh(); - mesh.require_shared_vertices(); - ModelInstance *mi = mo.instances[0]; - auto rotation = Vec3d::Zero(); - auto offset = Vec3d::Zero(); - Transform3d trafo_instance = Geometry::assemble_transform(offset, - rotation, - mi->get_scaling_factor(), - mi->get_mirror()); - mesh.transform(trafo_instance); +template +struct RotfinderBoilerplate { + static constexpr unsigned MAX_TRIES = MAX_ITER; - return mesh; -} + int status = 0; + TriangleMesh mesh; + unsigned max_tries; + const RotOptimizeParams ¶ms; + + // Assemble the mesh with the correct transformation to be used in rotation + // optimization. + static TriangleMesh get_mesh_to_rotate(const ModelObject &mo) + { + TriangleMesh mesh = mo.raw_mesh(); + mesh.require_shared_vertices(); + + ModelInstance *mi = mo.instances[0]; + auto rotation = Vec3d::Zero(); + auto offset = Vec3d::Zero(); + Transform3d trafo_instance = + Geometry::assemble_transform(offset, rotation, + mi->get_scaling_factor(), + mi->get_mirror()); + + mesh.transform(trafo_instance); + + return mesh; + } + + RotfinderBoilerplate(const ModelObject &mo, const RotOptimizeParams &p) + : mesh{get_mesh_to_rotate(mo)} + , params{p} + , max_tries(p.accuracy() * MAX_TRIES) + { + + } + + void statusfn() { params.statuscb()(++status * 100.0 / max_tries); } + bool stopcond() { return ! params.statuscb()(-1); } +}; Vec2d find_best_misalignment_rotation(const ModelObject & mo, const RotOptimizeParams ¶ms) { - static constexpr unsigned MAX_TRIES = 1000; - - // return value - XYRotation rot; - - // We will use only one instance of this converted mesh to examine different - // rotations - TriangleMesh mesh = get_mesh_to_rotate(mo); - - // To keep track of the number of iterations - int status = 0; - - // The maximum number of iterations - auto max_tries = unsigned(params.accuracy() * MAX_TRIES); - - auto &statuscb = params.statuscb(); - - // call status callback with zero, because we are at the start - statuscb(status); - - auto statusfn = [&statuscb, &status, &max_tries] { - // report status - statuscb(++status * 100.0/max_tries); - }; - - auto stopcond = [&statuscb] { - return ! statuscb(-1); - }; + RotfinderBoilerplate<1000> bp{mo, params}; // Preparing the optimizer. - size_t gridsize = std::sqrt(max_tries); - opt::Optimizer solver(opt::StopCriteria{} - .max_iterations(max_tries) - .stop_condition(stopcond), - gridsize); + size_t gridsize = std::sqrt(bp.max_tries); + opt::Optimizer solver( + opt::StopCriteria{}.max_iterations(bp.max_tries) + .stop_condition([&bp] { return bp.stopcond(); }), + gridsize + ); // We are searching rotations around only two axes x, y. Thus the // problem becomes a 2 dimensional optimization task. @@ -348,48 +345,19 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo, auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); auto result = solver.to_max().optimize( - [&mesh, &statusfn] (const XYRotation &rot) + [&bp] (const XYRotation &rot) { - statusfn(); - return get_misalginment_score(mesh, to_transform3f(rot)); + bp.statusfn(); + return get_misalginment_score(bp.mesh, to_transform3f(rot)); }, opt::initvals({0., 0.}), bounds); - rot = result.optimum; - - return {rot[0], rot[1]}; + return {result.optimum[0], result.optimum[1]}; } Vec2d find_least_supports_rotation(const ModelObject & mo, const RotOptimizeParams ¶ms) { - static const unsigned MAX_TRIES = 1000; - - // return value - XYRotation rot; - - // We will use only one instance of this converted mesh to examine different - // rotations - TriangleMesh mesh = get_mesh_to_rotate(mo); - - // To keep track of the number of iterations - unsigned status = 0; - - // The maximum number of iterations - auto max_tries = unsigned(params.accuracy() * MAX_TRIES); - - auto &statuscb = params.statuscb(); - - // call status callback with zero, because we are at the start - statuscb(status); - - auto statusfn = [&statuscb, &status, &max_tries] { - // report status - statuscb(unsigned(++status * 100.0/max_tries) ); - }; - - auto stopcond = [&statuscb] { - return ! statuscb(-1); - }; + RotfinderBoilerplate<1000> bp{mo, params}; SLAPrintObjectConfig pocfg; if (params.print_config()) @@ -397,31 +365,35 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, pocfg.apply(mo.config.get()); + XYRotation rot; + // Different search methods have to be used depending on the model elevation if (is_on_floor(pocfg)) { - std::vector inputs = get_chull_rotations(mesh, max_tries); - max_tries = inputs.size(); + std::vector inputs = get_chull_rotations(bp.mesh, bp.max_tries); + bp.max_tries = inputs.size(); // If the model can be placed on the bed directly, we only need to // check the 3D convex hull face rotations. - auto objfn = [&mesh, &statusfn](const XYRotation &rot) { - statusfn(); + auto objfn = [&bp](const XYRotation &rot) { + bp.statusfn(); Transform3f tr = to_transform3f(rot); - return get_supportedness_onfloor_score(mesh, tr); + return get_supportedness_onfloor_score(bp.mesh, tr); }; - rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); + rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] { + return bp.stopcond(); + }); } else { - // Preparing the optimizer. - size_t gridsize = std::sqrt(max_tries); // 2D grid has gridsize^2 calls - opt::Optimizer solver(opt::StopCriteria{} - .max_iterations(max_tries) - .stop_condition(stopcond), - gridsize); + size_t gridsize = std::sqrt(bp.max_tries); // 2D grid has gridsize^2 calls + opt::Optimizer solver( + opt::StopCriteria{}.max_iterations(bp.max_tries) + .stop_condition([&bp] { return bp.stopcond(); }), + gridsize + ); // We are searching rotations around only two axes x, y. Thus the // problem becomes a 2 dimensional optimization task. @@ -429,10 +401,10 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); auto result = solver.to_min().optimize( - [&mesh, &statusfn] (const XYRotation &rot) + [&bp] (const XYRotation &rot) { - statusfn(); - return get_supportedness_score(mesh, to_transform3f(rot)); + bp.statusfn(); + return get_supportedness_score(bp.mesh, to_transform3f(rot)); }, opt::initvals({0., 0.}), bounds); // Save the result @@ -442,7 +414,8 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, return {rot[0], rot[1]}; } -inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set& its, const Transform3f &tr) +inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set &its, + const Transform3f &tr) { if (its.vertices.empty()) return {}; @@ -458,38 +431,12 @@ inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set& its, const return {bmin.cast(), bmax.cast()}; } -Vec2d find_min_z_height_rotation(const ModelObject &mo, const RotOptimizeParams ¶ms) +Vec2d find_min_z_height_rotation(const ModelObject &mo, + const RotOptimizeParams ¶ms) { - static const unsigned MAX_TRIES = 1000; + RotfinderBoilerplate<1000> bp{mo, params}; - // return value - XYRotation rot; - - // We will use only one instance of this converted mesh to examine different - // rotations - TriangleMesh mesh = get_mesh_to_rotate(mo); - - // To keep track of the number of iterations - unsigned status = 0; - - // The maximum number of iterations - auto max_tries = unsigned(params.accuracy() * MAX_TRIES); - - auto &statuscb = params.statuscb(); - - // call status callback with zero, because we are at the start - statuscb(status); - - auto statusfn = [&statuscb, &status, &max_tries] { - // report status - statuscb(unsigned(++status * 100.0/max_tries) ); - }; - - auto stopcond = [&statuscb] { - return ! statuscb(-1); - }; - - TriangleMesh chull = mesh.convex_hull_3d(); + TriangleMesh chull = bp.mesh.convex_hull_3d(); chull.require_shared_vertices(); auto inputs = reserve_vector(chull.its.indices.size()); auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) { @@ -514,18 +461,20 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo, const RotOptimizeParams } inputs.shrink_to_fit(); - max_tries = inputs.size(); + bp.max_tries = inputs.size(); // If the model can be placed on the bed directly, we only need to // check the 3D convex hull face rotations. - auto objfn = [&chull, &statusfn](const XYRotation &rot) { - statusfn(); + auto objfn = [&bp, &chull](const XYRotation &rot) { + bp.statusfn(); Transform3f tr = to_transform3f(rot); return bounding_box_with_tr(chull.its, tr).size().z(); }; - rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); + XYRotation rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] { + return bp.stopcond(); + }); return {rot[0], rot[1]}; } diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index d703ff0e36..811c9c97e5 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -27,9 +27,9 @@ class RotoptimizeJob : public PlaterJob "structures.\nNote that this method will try to find the best surface of the object " "for touching the print bed if no elevation is set.")}, // Just a min area bounding box that is done for all methods anyway. - {L("Smallest Z height"), + {L("Lowest Z height"), sla::find_min_z_height_rotation, - L("Rotate the model to have least z height for faster print time.")}}; + L("Rotate the model to have the lowest z height for faster print time.")}}; size_t m_method_id = 0; float m_accuracy = 0.75; From a3b089eceb9896fcb68bb431358f806df76aff59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Aug 2021 16:51:14 +0200 Subject: [PATCH 158/240] Remove misleading comment --- src/libslic3r/SLA/Rotfinder.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index ac336731af..5486741f26 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -463,9 +463,6 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo, inputs.shrink_to_fit(); bp.max_tries = inputs.size(); - // If the model can be placed on the bed directly, we only need to - // check the 3D convex hull face rotations. - auto objfn = [&bp, &chull](const XYRotation &rot) { bp.statusfn(); Transform3f tr = to_transform3f(rot); From 81113218ccb41a2db727d744d377ecc54ae9efe4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 19 Aug 2021 09:38:51 +0200 Subject: [PATCH 159/240] Added retract acceleration member variable to GCodeProcessor and modified GCodeProcessor::process_M204() method --- src/libslic3r/GCode/GCodeProcessor.cpp | 47 ++++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 8 +++++ src/libslic3r/Technologies.hpp | 2 ++ 3 files changed, 57 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index b5c10823e4..9f853d7b4a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -24,6 +24,9 @@ static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 +#if ENABLE_RETRACT_ACCELERATION +static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 +#endif // ENABLE_RETRACT_ACCELERATION static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f; static const size_t MIN_EXTRUDERS_COUNT = 5; @@ -178,6 +181,10 @@ void GCodeProcessor::TimeMachine::reset() enabled = false; acceleration = 0.0f; max_acceleration = 0.0f; +#if ENABLE_RETRACT_ACCELERATION + retract_acceleration = 0.0f; + max_retract_acceleration = 0.0f; +#endif // ENABLE_RETRACT_ACCELERATION travel_acceleration = 0.0f; max_travel_acceleration = 0.0f; extrude_factor_override_percentage = 1.0f; @@ -834,6 +841,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; +#if ENABLE_RETRACT_ACCELERATION + float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i); + m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration; + m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION; +#endif // ENABLE_RETRACT_ACCELERATION float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i); m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration; m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; @@ -1052,6 +1064,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; +#if ENABLE_RETRACT_ACCELERATION + float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i); + m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration; + m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION; +#endif // ENABLE_RETRACT_ACCELERATION float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i); m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration; m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; @@ -2713,14 +2730,22 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) set_acceleration(static_cast(i), value); set_travel_acceleration(static_cast(i), value); if (line.has_value('T', value)) +#if ENABLE_RETRACT_ACCELERATION + set_retract_acceleration(static_cast(i), value); +#else set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); +#endif // ENABLE_RETRACT_ACCELERATION } else { // New acceleration format, compatible with the upstream Marlin. if (line.has_value('P', value)) set_acceleration(static_cast(i), value); if (line.has_value('R', value)) +#if ENABLE_RETRACT_ACCELERATION + set_retract_acceleration(static_cast(i), value); +#else set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); +#endif // ENABLE_RETRACT_ACCELERATION if (line.has_value('T', value)) // Interpret the T value as the travel acceleration in the new Marlin format. set_travel_acceleration(static_cast(i), value); @@ -2979,10 +3004,32 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode } } +#if ENABLE_RETRACT_ACCELERATION +float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const +{ + size_t id = static_cast(mode); + return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION; +} +#else float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast(mode)); } +#endif // ENABLE_RETRACT_ACCELERATION + +#if ENABLE_RETRACT_ACCELERATION +void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value) +{ + std::cout << value << "\n"; + + size_t id = static_cast(mode); + if (id < m_time_processor.machines.size()) { + m_time_processor.machines[id].retract_acceleration = (m_time_processor.machines[id].max_retract_acceleration == 0.0f) ? value : + // Clamp the acceleration with the maximum. + std::min(value, m_time_processor.machines[id].max_retract_acceleration); + } +} +#endif // ENABLE_RETRACT_ACCELERATION float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 999af481de..4fcdd8df39 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -242,6 +242,11 @@ namespace Slic3r { float acceleration; // mm/s^2 // hard limit for the acceleration, to which the firmware will clamp. float max_acceleration; // mm/s^2 +#if ENABLE_RETRACT_ACCELERATION + float retract_acceleration; // mm/s^2 + // hard limit for the acceleration, to which the firmware will clamp. + float max_retract_acceleration; // mm/s^2 +#endif // ENABLE_RETRACT_ACCELERATION float travel_acceleration; // mm/s^2 // hard limit for the travel acceleration, to which the firmware will clamp. float max_travel_acceleration; // mm/s^2 @@ -669,6 +674,9 @@ namespace Slic3r { float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const; float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; +#if ENABLE_RETRACT_ACCELERATION + void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); +#endif // ENABLE_RETRACT_ACCELERATION float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f6366c18e7..02d8fccd45 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -47,6 +47,8 @@ #define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0) // Enable drawing contours, at cut level, for sinking volumes #define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0) +// Enable implementation of retract acceleration in gcode processor +#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ From f44d2e14a19900c89023fcbc4934365546526a70 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 19 Aug 2021 09:47:01 +0200 Subject: [PATCH 160/240] Removed debug code --- src/libslic3r/GCode/GCodeProcessor.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9f853d7b4a..0d71a19f53 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3020,8 +3020,6 @@ float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMo #if ENABLE_RETRACT_ACCELERATION void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value) { - std::cout << value << "\n"; - size_t id = static_cast(mode); if (id < m_time_processor.machines.size()) { m_time_processor.machines[id].retract_acceleration = (m_time_processor.machines[id].max_retract_acceleration == 0.0f) ? value : From 12328a74f70fdd1b11153dcb8f1787bfe5330143 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 19 Aug 2021 10:15:42 +0200 Subject: [PATCH 161/240] Desktop integration changes empty catch block fix internal namespace fix wrong app config var Desktop integration for regular executables cmake option SLIC3R_DESKTOP_INTEGRATION cmake dependent option escape executable path in desktop file by adding /' Error messages instead of notifications. --- CMakeLists.txt | 7 ++ src/slic3r/GUI/DesktopIntegrationDialog.cpp | 94 ++++++++++----------- src/slic3r/GUI/GUI_App.cpp | 8 +- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a69d3bbfc..a7d3dbf924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(PrusaSlicer) include("version.inc") include(GNUInstallDirs) +include(CMakeDependentOption) set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources") file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN) @@ -32,6 +33,8 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. +CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 0 "NOT SLIC3R_FHS" 0) set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.") @@ -71,6 +74,10 @@ if (SLIC3R_GUI) add_definitions(-DSLIC3R_GUI) endif () +if(SLIC3R_DESKTOP_INTEGRATION) + add_definitions(-DSLIC3R_DESKTOP_INTEGRATION) +endif () + if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(IS_CLANG_CL TRUE) diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index a2f7c89337..1ec008bd51 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -1,15 +1,18 @@ #ifdef __linux__ #include "DesktopIntegrationDialog.hpp" #include "GUI_App.hpp" +#include "GUI.hpp" #include "format.hpp" #include "I18N.hpp" #include "NotificationManager.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Platform.hpp" +#include "libslic3r/Config.hpp" #include #include +#include #include #include @@ -17,9 +20,9 @@ namespace Slic3r { namespace GUI { -namespace integrate_desktop_internal{ +namespace { // Disects path strings stored in system variable divided by ':' and adds into vector -static void resolve_path_from_var(const std::string& var, std::vector& paths) +void resolve_path_from_var(const std::string& var, std::vector& paths) { wxString wxdirs; if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() ) @@ -34,7 +37,7 @@ static void resolve_path_from_var(const std::string& var, std::vectorget("desktop_integration_app_path")); BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path; @@ -126,10 +125,6 @@ bool DesktopIntegrationDialog::is_integrated() } bool DesktopIntegrationDialog::integration_possible() { - - const char *appimage_env = std::getenv("APPIMAGE"); - if (!appimage_env) - return false; return true; } void DesktopIntegrationDialog::perform_desktop_integration() @@ -143,14 +138,26 @@ void DesktopIntegrationDialog::perform_desktop_integration() try { appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); } catch (std::exception &) { + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - boost::filesystem::canonical did not return appimage path."; + show_error(nullptr, _L("Performing desktop integration failed - boost::filesystem::canonical did not return appimage path.")); + return; } } else { - // not appimage - not performing - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable."; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); - return; + // not appimage - find executable + appimage_path = boost::dll::program_location().string(); + //appimage_path = wxStandardPaths::Get().GetExecutablePath().string(); + BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << appimage_path; + if (appimage_path.empty()) + { + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - no executable found."; + show_error(nullptr, _L("Performing desktop integration failed - Could not find executable.")); + return; + } } + // Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'appimage_path' + //appimage_path = std::regex_replace(appimage_path, std::regex("\'"), "\\\'"); + // Find directories icons and applications // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. @@ -158,8 +165,8 @@ void DesktopIntegrationDialog::perform_desktop_integration() // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. std::vectortarget_candidates; - integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates); - integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates); + resolve_path_from_var("XDG_DATA_HOME", target_candidates); + resolve_path_from_var("XDG_DATA_DIRS", target_candidates); AppConfig *app_config = wxGetApp().app_config; // suffix string to create different desktop file for alpha, beta. @@ -186,7 +193,6 @@ void DesktopIntegrationDialog::perform_desktop_integration() icon_theme_dirs = "/hicolor/96x96/apps"; } - std::string target_dir_icons; std::string target_dir_desktop; @@ -194,24 +200,24 @@ void DesktopIntegrationDialog::perform_desktop_integration() // iterate thru target_candidates to find icons folder for (size_t i = 0; i < target_candidates.size(); ++i) { // Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/ - if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) { + if (contains_path_dir(target_candidates[i], "icons")) { target_dir_icons = target_candidates[i]; std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + if (copy_icon(icon_path, dest_path)) break; // success else target_dir_icons.clear(); // copying failed // if all failed - try creating default home folder if (i == target_candidates.size() - 1) { // create $HOME/.local/share - integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs); // copy icon target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons") - || !integrate_desktop_internal::copy_icon(icon_path, dest_path)) { + if (!contains_path_dir(target_dir_icons, "icons") + || !copy_icon(icon_path, dest_path)) { // every attempt failed - icon wont be present target_dir_icons.clear(); } @@ -228,7 +234,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() // iterate thru target_candidates to find applications folder for (size_t i = 0; i < target_candidates.size(); ++i) { - if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) { + if (contains_path_dir(target_candidates[i], "applications")) { target_dir_desktop = target_candidates[i]; // Write slicer desktop file std::string desktop_file = GUI::format( @@ -236,7 +242,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=PrusaSlicer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer%2%\n" - "Exec=%3% %%F\n" + "Exec=\'%3%\' %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n" @@ -246,23 +252,23 @@ void DesktopIntegrationDialog::perform_desktop_integration() "StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){ + if (create_desktop_file(path, desktop_file)){ BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success."; break; } else { // write failed - try another path - BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed."; + BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicer.desktop file installation failed. failed path: " << target_candidates[i]; target_dir_desktop.clear(); } // if all failed - try creating default home folder if (i == target_candidates.size() - 1) { // create $HOME/.local/share - integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); // create desktop file target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir()); std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) { - if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) { + if (contains_path_dir(target_dir_desktop, "applications")) { + if (!create_desktop_file(path, desktop_file)) { // Desktop file not written - end desktop integration BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file"; return; @@ -278,7 +284,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() if(target_dir_desktop.empty()) { // Desktop file not written - end desktop integration BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory"; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + show_error(nullptr, _L("Performing desktop integration failed - could not find applications directory.")); return; } // save path to desktop file @@ -290,7 +296,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() { std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir()); std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix); - if (integrate_desktop_internal::copy_icon(icon_path, dest_path)) + if (copy_icon(icon_path, dest_path)) // save path to icon app_config->set("desktop_integration_icon_viewer_path", dest_path); else @@ -303,7 +309,7 @@ void DesktopIntegrationDialog::perform_desktop_integration() "Name=Prusa Gcode Viewer%1%\n" "GenericName=3D Printing Software\n" "Icon=PrusaSlicer-gcodeviewer%2%\n" - "Exec=%3% --gcodeviwer %%F\n" + "Exec=\'%3%\' --gcodeviwer %%F\n" "Terminal=false\n" "Type=Application\n" "MimeType=text/x.gcode;\n" @@ -312,23 +318,17 @@ void DesktopIntegrationDialog::perform_desktop_integration() "StartupNotify=false", name_suffix, version_suffix, appimage_path); std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix); - if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file)) + if (create_desktop_file(desktop_path, desktop_file)) // save path to desktop file app_config->set("desktop_integration_app_viewer_path", desktop_path); else { - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file"; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail); + BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file"; + show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully.")); } wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); } void DesktopIntegrationDialog::undo_desktop_intgration() { - const char *appimage_env = std::getenv("APPIMAGE"); - if (!appimage_env) { - BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable."; - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail); - return; - } const AppConfig *app_config = wxGetApp().app_config; // slicer .desktop std::string path = std::string(app_config->get("desktop_integration_app_path")); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 91d16c602b..06d805eeb1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1806,10 +1806,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots")); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot")); local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates")); -#ifdef __linux__ - if (DesktopIntegrationDialog::integration_possible()) - local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); -#endif +#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) + //if (DesktopIntegrationDialog::integration_possible()) + local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration")); +#endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) local_menu->AppendSeparator(); } local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots + From b686b5764efbc49ab4bc6f5ccabb496bddd893d1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 19 Aug 2021 10:22:26 +0200 Subject: [PATCH 162/240] Fix opening of gizmos after clicking on info lines in object list when object has instances --- src/slic3r/GUI/GUI_ObjectList.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 318705e09e..0c555454d3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3404,6 +3404,18 @@ void ObjectList::update_selections_on_canvas() std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); } + else if (type == itInfo) { + // When selecting an info item, select one instance of the + // respective object - a gizmo may want to be opened. + int inst_idx = selection.get_instance_idx(); + int scene_obj_idx = selection.get_object_idx(); + mode = Selection::Instance; + // select first instance, unless an instance of the object is already selected + if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx) + inst_idx = 0; + std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); + volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + } else { mode = Selection::Instance; @@ -3418,7 +3430,7 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); - if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo)) + if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else add_to_selection(item, selection, instance_idx, mode); From 97418cf9f59e1f78f554975473b02eb50980b975 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 19 Aug 2021 10:27:23 +0200 Subject: [PATCH 163/240] Desktop integration in config wizard ifdef --- src/slic3r/GUI/ConfigWizard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index f64bdecbaa..bc56fe97ee 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -494,7 +494,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent) { welcome_text->Hide(); cbox_reset->Hide(); -#ifdef __linux__ +#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) if (!DesktopIntegrationDialog::is_integrated()) cbox_integrate->Show(true); else From c08b4be0e650fd47e073daea9fb1bb3971fc9829 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 19 Aug 2021 11:00:36 +0200 Subject: [PATCH 164/240] Fix of missplaced checkbox Perform desktop integration in config wizard. --- src/slic3r/GUI/ConfigWizard.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index bc56fe97ee..3f388f4850 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -494,15 +494,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent) { welcome_text->Hide(); cbox_reset->Hide(); -#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) - if (!DesktopIntegrationDialog::is_integrated()) - cbox_integrate->Show(true); - else - cbox_integrate->Hide(); -#else - cbox_integrate->Hide(); -#endif - + cbox_integrate->Hide(); } void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) @@ -510,7 +502,7 @@ void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY; welcome_text->Show(data_empty); cbox_reset->Show(!data_empty); -#ifdef __linux__ +#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION) if (!DesktopIntegrationDialog::is_integrated()) cbox_integrate->Show(true); else From 316d38807d64e377c4b1d3a71f8c9587dcc82066 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 19 Aug 2021 11:54:41 +0200 Subject: [PATCH 165/240] Simplified shader printbed.vs --- resources/shaders/printbed.vs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs index ac47637820..ee19098c1c 100644 --- a/resources/shaders/printbed.vs +++ b/resources/shaders/printbed.vs @@ -1,12 +1,12 @@ #version 110 -attribute vec4 v_position; +attribute vec3 v_position; attribute vec2 v_tex_coords; varying vec2 tex_coords; void main() { - gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * v_position; + gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0); tex_coords = v_tex_coords; } From adfb5b6b4960224c23fec9531765f8d9fdf7ff45 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 19 Aug 2021 12:12:25 +0200 Subject: [PATCH 166/240] Small refactoring into GLModel::render() --- src/slic3r/GUI/GLModel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 6d54ec20e4..a9550bc04f 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -169,6 +169,8 @@ void GLModel::reset() void GLModel::render() const { + GLShaderProgram* shader = wxGetApp().get_current_shader(); + for (const RenderData& data : m_render_data) { if (data.vbo_id == 0 || data.ibo_id == 0) continue; @@ -190,7 +192,6 @@ void GLModel::render() const glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) shader->set_uniform("uniform_color", data.color); else From 6d44bde96d6e11e22d34beaa85a9d480ec7c2590 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 19 Aug 2021 12:36:18 +0200 Subject: [PATCH 167/240] Progress notification: fix of wrong method call. --- src/slic3r/GUI/NotificationManager.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index b347c9dfe4..2bc9aaaabf 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -391,7 +391,7 @@ private: { public: - ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); } + ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { } virtual void set_percentage(float percent) { m_percentage = percent; } protected: virtual void init() override; @@ -434,6 +434,7 @@ private: , m_file_size(filesize) { m_has_cancel_button = true; + set_percentage(percentage); } static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; } void set_percentage(float percent) override; From 8f58b11b085925774fd3172bca339b4f15d1412a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 19 Aug 2021 14:01:54 +0200 Subject: [PATCH 168/240] Rotated arrow asset. --- resources/icons/toolbar_arrow.png | Bin 3996 -> 3996 bytes resources/icons/toolbar_arrow.svg | 83 ++++++++++++++++++---- src/slic3r/GUI/GLToolbar.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png index 90ddce38f0c0dfa71dc4d6abdf07fd8a74b43a01..a370442ff0e933b85d436f3abac584b2e5ac3f37 100644 GIT binary patch literal 3996 zcmeHK_cI&-)4qrn9EskEe$hM8+i6j94yQ&ZN{F5l5vPR^33Ac7aD;G3^p+e)ln}ig z=afW?UZcLg-@gCheP?#(+1+_|W@mqRb~e%6R3AviP6Yq}fQAOTkZTMF0B(>|l3zy{ zm4}npa3ct!uMNNqa;{w`q@G$PS^z*zD)qTD*>!#^z`!O50HE#sKfm$A|C8Hwku6vc z7Hr|~5gg_m=ne=A3zK;K#3#trIlx`QKhQI0OO^ea{nJoa3mWdYnRhF|u8ZwQSb^Az zda&S)G#G>=dPLhUUVDv9ycUht)FFw^Xo-c@-{p)0O*U?7uDvw`;AMbh@f`G&XmPbw zt&-qE9aJrixsz{yC?tCdnE^1gH-_W=58H=0m1@xM4-S5x0T(x;ZoWa!N1PqCHMFE5 zq#_Sx&5mG%P?15zhxb)uBGMva|5`PaBHDc&k8ez*#TGl&nI{{^bySIhiXtLnc07%y zC-yWWR^(xNw1EiA{auFii@6I(PYtXt40>5m8ncUAgXtt)Dcb28&>jS*z%b;YPeEOe z*GhVTFdZt0{*LCwY-4TUA*+4@ec#{=MV!^)IfajbCO9A}(knkF#q_6XQ*<<*p}PwXzHA>u1a6ACDonU{#b5_<{5XMZu<4WZ`;Jx z(h^cMuHk1Qctiw^6rY+1fBi zjs1Hlnh1@8&1al6ryC_h6ie7YmO7Q+FW=(K^?j8~nlsPc`W;@B*DJoBqXyosl*Y zFcRh9fO4To4qx@{Vj~o@pzE-x6qL;JDQ*15+$b?^6(mA?b%s3RY?ZaKD^J(1oZFCU zVV|VlY7v813zuG!cY#a?q%zE^OUz*TMI_=}98xXJ){4U$Rhi1P%b55@!f#~3;~}1K zZTSJNnCZGk+np2CjFPYVn!x1K2YvP1-@o`gtt3Llo`Y&zSe$}E>mzQi%Puq+-1`+` zu*I?go>@}+X|9DVNnZplAxjIr%o4LJF=rOQg<;5bWX|{YToyG?zAw*T9EgzL@Ij5r zm!XSzzWM}MG63Hc=hbY*Ev_zG<=g0l9!ZZse-8ARB;LcW!47PP&i#@tOgcPZFl#y*50p3YCwH0Ds4Z7BB zx8dlVJZQSC=T2@Tud0>dt^ngek9-^>NGz)Z(gzO@y7-&qFlnv3vz)kRipD);O(`s1 z$GJ$Kb2v(>=((N*B0V=-jA=Pjn<_FK0?MV~v~r~%oaDOBAc-3__DWPo>Fm$QYmZ9& zmAP#;xV}>LyQ3=*7RD>j?Li@mU<;AEzvFhcxqs>{d@K6QGUKhL2wEA-ywObYnu47V z$l4E{Cf#6r+2^{UqFRzu&J;Z%5vi^SuCCljt)67%*`ZCdF3IM5^c}|BCp5+1Fh`No znd?R=c)3oO@{KnAA&H+uLx)F7dh&v0Jxr}S_>Wemv8fH|k^zp)R0dq|MV8t~-iG6i z!h4G)NyJDF%sq*1WXYi@n@+Mfw|oxNM0AThf!%$t&i=rqb2#G34hGmfX(v}OS@$X z)XjF@mltL$n#?Ivd!(Sl;NU@meG-{hDcqLjR^dKv*50uEme*q`L-$%G4n&8@v$p0u zh+{2Yd*xx|QXiPwEXKz?n00FU%cQ{$B|`gNWkT5;EV;=8S%PsRD-K29s-b~4g_ZiL zwH2%uR!g`ibutlq%|FH!2i|9^+5H}*f&arDkU}&f8jNByz>`%_d zkn2lRfX?v3U5i{t-vHg*+0j|1gAOl4Bj+jGFITxP0Z?UWnn{qbs{}hg;*8^C!uPJS z-BQ7TN*IsVntY^iFJ<7kXjZPdr}nNxX*cV>+h98nq2=8IwI20EKRc|$ky_6+B9DA9 zs1jR`i(z%>tqng0qblOe<=`!hm?kqbc`&8}#;8Y);o_vr<9ViZJ@ImS{^y#%-Xyz& zESkM_tOF=qWiM>`b0$@Z2(}C+N4_$*FO|)=`R<5sdSv&}PmJ);M5K!ER#=*}>X3oP zm!=vCL=!I+v+C{F+yHGatburM*+YndTKK#`bcLT;S` zg1rV_7!Buy-|+BkG=3T+sL#vXS@E8uUT4MN(GQ_Kc3I6w%N7jrtr}fQ-e7Uq@s@9Y z5f6c7Joqo|--}=W6a`Ai9CIkq!0j1!4~~MlEw7gmlT_)V_7Zko@5ux7s_vX!T#qfJeTA_Qr`6g_IQ8TE)r#Y~hYw zy4+Epwro!TCRKjQS~f?vZTIf{Np*u^;p9#!&{Un_zYjt{TeP*IYa6Q<NcO!uj)j5QMu3gu)-?NqKv+nkp6bI@=5(`%IfE%YPXe^o$D3r^aaI~XCo z9NN42D^fC@L?7eT$HgU+^#@ds=Zl2-U~BD!xWdlWLS?0f^FEeszC*o!wD$b#UuC-i zHsueh{GNHu4#Rz`4C&=Vw^T1)yLudHz4+K99&)v!c0F0&{eB39Gi8`GrruAy z;%Ixek`#4>!y9Gid12-{?o=d4TINE%Z9cVGNSgI9YF5{st&DhiTE_)5_<7tbhNdn$ zu$NFiAEj5sqCCCb)~u-6V;iIx-!N8rc0W;UgU`K4%=g-QINfE$YLmSyZVcNCW2O2X zsUpn$6W0D%)T6{8n5Wwnq_$GlNFfVR_qk<9xY?yuTEg4@S%nhjL4%^u6%jGPjLZ^YIMLs*r;LnkX-Y-tn2a zSPf{{`^yUHHyOf1WkHg*1ydK1iF!^Z^9uM$dcA;Wg0>`VSI^ZL$u=_SQ5s9c54i7=H=JT-0BQ2IlWpN}{F8m)dh!-wVU|X;ab~K3G&TNQITJ-qEkyf2`)}I- z8`j8HS&TU*#ZKn!9s8vk2uv(q;7PA0cqc}e}5b~E!C+u%q znU@S$Ax1BLOpff|^MHIeHKwaG4~L+7LX46&7JzTC>#2)es0!+v*QZ=6J*Jq*MI)B? z>}^F^o32QR46c(Xf6I}hI89OqU)Rl&+h(g!)xUm)6w*GY>-ooGU>LoCzkYrJhI*#D z7;Ps+6=7j~FS^c&1a3MjoIcZN)f7O|jt_g$#yOV-zq{hGqIm2NpZ&ZT54ob~>jBv( LTMD)O|Nj2~#I)V2 literal 3996 zcmeH~_fr$l(uPBkUM@x=0)nE1B2@^80#bqq1}s!5p@+~>S`1QyXy{6nKqw+D^cp~- zp$k%l&1qMW`UIEH8%AeM15E&+GM@d|?!tM`>izJ!4*@QJl%X8?7W?1yk0uvHdOfm0B#OFsHQOrzMjeMZDn$$ zv%dky>G7&&+~66LexkwM&~JrZ4vbDrzEGa5n=39XI9FeafNVH)L=Vq}L1CVj@$VWl)<-AP!f9WQ9LIh~qq=TTVc7q{{1<=Ak= zH+f4_4*z;V?8~q5?Je^cvz-yous8#IhI?dG_ej~QRQoKn93dD=QdIeRBA#ge&2Z~? zUl%J;h4N^wW6;nuN1{43)7Gr0|D*j=M-X)JX)YCr)RIqxhlU;+4~a5P8=H-lR|yiGiyFw zwN;Ta`r125F*cvg2!bZ_Iz!j9T;f#Uo$W@|uXbq6vHKNZKGqKS*k=|2yBXK#j!7Ls zaWAAABDA@_i>mBNCivcd3Gp=i^fHHbD+rt@NbH5?z#Kmac;y6n*ym&*G9<;QgTenf z4R_lfnOpo|tUE7g1d_s};>J-NHi%i`wSsxdQ?Az+b{Nai$Fw&=%eDbM@4;*-5BuT; z!*QGFB5`nnr7m5DyJVD;ecuQ{xj*Z)4^;nzzT8UQ&r2*@h&)Sz8@wlO=`}J5uY8^* zBLV{rN7c6ezOfdMrQv|74sHi5z-V29wqDm1T#|>gfP)13m!hZj-tn%5Y)jnI6>v)T z%}XVT;3N+JgmOc_+F&x&E@L2;W8jbrD4m_iN+ouOE~57rs3XI#S^(c~v^M6$AtMGh|2GiFw}#-GIkJg#7udW+3JV<*6Py+}um+ z`vbzBk`eNJYsjXmcepD}bQFllthhIPVj^X z!H*_F-I@0$SpwrcQT&(8Q^Rz(a*fT7I$@WlbgEXf83fl$TI|0ODjAjhwVMS_HQF;z zC>^9WYm`a;uU`8VSFvr`yNqHeblERLK{A+uh45Es546&D#)8&q4Lhsi1}Pv z3~m*&poae+gV!Dhk3cQmN6XTwzMDSfxD4mZJic4Mt!NX{>+i+NS&%IA|@%GP@P-33?IKy3;E%&3* z(n;e2>x!xgm#u<0j4`c_JJ}@YM?7|;Mu!I(3>i}j1pY%@x6x=aBMk0+xn!4q+ zCj2u~W>PAfk|$9us!;B6o7mTCUZeER*8JD7z}b6=_-@Y&z?msV&WB@o+k#3+gUHB; z#dZY4*AHJW3r8dqGwq7k04qgLjCxGKv3i=}tcPzHoM(i8?OGHSu(jXwzapaeia);eACH@i8y^ z-iii-C_POzv^{r!IdCQvKE@Zzt0rHScORj@sUaAffo(s2rW2ik+i;h5H86x^S8fR2 z;kF-KS>Y-jImtnmt;jY4^Xl_N9CdRCvC3KD(vZGs) zj+|PH*d4(u~o`UMuHHuV0?L4{T1oMiKijdN;T&Uv6ZmyE3vV%>$R0@A#4Ar<{WPQ}$D z8ybh64+}(WI?YaNuyuTegns<_wnQSTk$$?eTH{itkRMR34S* zn5q+0)b)jy4s@<9Q3pfFjmZ=5+88B`qf8_|pO$iEl(rgrQ>da-KFFvF(~9 zqpa+87+3Lr+6g^Fu!Qh}^h->Fpn=Iv=}}+dC6zDmdzuz>$iUpw8#Bl?eP z8R~AX|FFB%1RSY;i#l!^N#VlStk0YU<+w!n+vH4 zUp30~5LO;!TP{;tp)+qLM7WQtIQ#qfQU$=NnNe(D{-fC-@sCU$9v8m>SBigp_V90% zv#1phJ=zwHMwZI{kiA)WjrTH)!)ZGGEzVlV%32bc%Nl9N3DQrYzZ*gr*|72`O?iuU zP`px}YpkA z6q+tzlq3~F2(SLUKfPB+1}@hJP1ap2ZgkLYW{967eW5_(XyfpZ28);2VqujT(n0+n z+wpbD1`9@w>5D7T&#j^N;l(n13*p_aXxA^v5|SjkVc?9T)S3B->uj`}4f44=`(NL} zL6gc;yN|D2?iBW+k8qERv9gT78PeXWw>Eok5!R@Oq8(N;&I# zSczUmw4aA|$ZP=E^r{UjxTW`JIT>@H45siM4n`t>nDq<&$+WdV!Z(KpBa~Km61!nP zSQ{4?1!C1(0>ZIDsU?qRiy8%g26eY{bv{*CE`oxOm~7a<+qYI~_C&8cX);Km_EWVE zFHEz6zjW>_HyP|opmoI4x7MeZ+{n=v_$WZ^;I(F|JSy>a% z7e%pT;GNcZ8;|?^5V15Nw*<`bjksGuM&x$?Q7c#OG%Mh6hHW_heMz#qK(v$PPlbyI z2%_xiEud>;`X$)BvpzDY8N2X%F(*G_-AjzX0=bpO(#(H@yr5ou(Qj|OVZn;d+|boR zt~ZT^*r0;)5ZtN!D=HUpLaizXNQptD)4J}r4w@)#J8lMw{;-w;_^JF2I6>oTuDQbH zG;LTuYfhd#06yV{JeY%=d3Z>jn;+@UD}+uFipxDqG)gS|YQ0mz1B*xp!?ADSzuH9T zM*Zv%qGo^;plP^EP6O^uJZ-sl$wh%ip;Cv1>rWnb6qEJkG^cOPFd9FdJ_A^Y3LK3d zp - - - and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); std::string marker_e(1, ImGui::ColorMarkerEnd); @@ -319,6 +338,8 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) } } } + if (create_pot) + write_pot(pot_elements); } HintData* HintDatabase::get_hint(bool up) { From 0be2e3bc716ae997d156c1faf01b0e1f3ae7aed5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 23 Aug 2021 21:16:58 +0200 Subject: [PATCH 203/240] Fixup of e5f0099 --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 29368312a3..97ffc6cb7e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4878,7 +4878,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename) _L("Select an action to apply to the file") + ": " + from_u8(filename)), 0, wxEXPAND | wxALL, 10); m_action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")), - static_cast(LoadType::OpenProject) - 1, static_cast(LoadType::LoadConfig)) - 1; + static_cast(LoadType::OpenProject), static_cast(LoadType::LoadConfig)) - 1; wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action")); if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT); From b3010a817bdc8e63b2ad2f9d281be3ba89216ba7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 07:46:59 +0200 Subject: [PATCH 204/240] Do not allow objects to be placed fully below bed --- src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Selection.cpp | 33 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/Selection.hpp | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 11dcfa7757..1dd16ee917 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1177,6 +1177,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2); #endif /* NDEBUG */ static const float SINKING_Z_THRESHOLD = -0.001f; +static const double SINKING_MIN_Z_THRESHOLD = 0.05; } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 9c0341ff45..7a9fdc3886 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -595,7 +595,7 @@ bool GLVolume::is_sinking() const bool GLVolume::is_below_printbed() const { - return transformed_convex_hull_bounding_box().max(2) < 0.0; + return transformed_convex_hull_bounding_box().max.z() < 0.0; } #if ENABLE_SINKING_CONTOURS diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b9aefcc76b..498b026056 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -621,7 +621,7 @@ void MainFrame::update_title() if (!dirty_marker.empty() || !project.empty()) { if (!dirty_marker.empty() && project.empty()) project = _("Untitled"); - title = dirty_marker + project + " - "; + title = dirty_marker + project + " - "; } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index dbc2594440..acb1f11131 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -683,7 +683,8 @@ void Selection::translate(const Vec3d& displacement, bool local) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH - this->set_bounding_boxes_dirty(); + ensure_not_below_bed(); + set_bounding_boxes_dirty(); } // Rotate an object around one of the axes. Only one rotation component is expected to be changing. @@ -1712,7 +1713,7 @@ void Selection::calc_unscaled_instance_bounding_box() const if (volume.is_modifier) continue; Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix(); - trafo.translation()(2) += volume.get_sla_shift_z(); + trafo.translation().z() += volume.get_sla_shift_z(); unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo)); } } @@ -1729,7 +1730,7 @@ void Selection::calc_scaled_instance_bounding_box() const if (volume.is_modifier) continue; Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix(); - trafo.translation()(2) += volume.get_sla_shift_z(); + trafo.translation().z() += volume.get_sla_shift_z(); scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo)); } } @@ -2134,6 +2135,32 @@ void Selection::ensure_on_bed() } } +void Selection::ensure_not_below_bed() +{ + typedef std::map, double> InstancesToZMap; + InstancesToZMap instances_max_z; + + for (size_t i = 0; i < m_volumes->size(); ++i) { + GLVolume* volume = (*m_volumes)[i]; + if (!volume->is_wipe_tower && !volume->is_modifier) { + const double max_z = volume->transformed_convex_hull_bounding_box().max.z(); + std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::iterator it = instances_max_z.find(instance); + if (it == instances_max_z.end()) + it = instances_max_z.insert(InstancesToZMap::value_type(instance, -DBL_MAX)).first; + + it->second = std::max(it->second, max_z); + } + } + + for (GLVolume* volume : *m_volumes) { + std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::iterator it = instances_max_z.find(instance); + if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD) + volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second); + } +} + bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const { struct SameInstance diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 0e0e869ef6..b3304571f8 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -385,6 +385,7 @@ public: private: void ensure_on_bed(); + void ensure_not_below_bed(); bool is_from_fully_selected_instance(unsigned int volume_idx) const; void paste_volumes_from_clipboard(); From d7d4d528439d03c43ec4b4fe90721a01b122b2c5 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 24 Aug 2021 08:21:14 +0200 Subject: [PATCH 205/240] Fix ../src/libslic3r/QuadricEdgeCollapse.cpp:565:22: warning: comparison of integer expressions of different signedness: 'const int' and 'uint32_t' {aka 'unsigned int'} [-Wsign-compare] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:48:15: warning: unused variable 'min_triangle_count' [-Wunused-variable] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:160:9: warning: unused variable 'wanted_count' [-Wunused-variable] ..\src\slic3r\GUI\Gizmos\GLGizmoSimplify.cpp(167): warning C4305: '=': truncation from 'double' to 'float' --- src/libslic3r/QuadricEdgeCollapse.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index c4e874c65e..6efc5f4a9d 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -562,7 +562,7 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi, if (e_info.t_index == ti1) continue; // ti1 will be deleted const Triangle &t = indices[e_info.t_index]; for (size_t i = 0; i < 3; ++i) - if (t[i] == vi) return true; + if (static_cast(t[i]) == vi) return true; } return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index a6f4f97a64..f0336f729e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -45,7 +45,6 @@ void GLGizmoSimplify::on_render_for_picking() {} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { - const int min_triangle_count = 4; // tetrahedron const int max_char_in_name = 20; create_gui_cfg(); @@ -119,11 +118,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(m_configuration.use_count); ImGui::Text(_L("Detail level").c_str()); std::vector reduce_captions = { - _u8L("Extra high"), - _u8L("High"), - _u8L("Medium"), - _u8L("Low"), - _u8L("Extra low") + static_cast(_u8L("Extra high")), + static_cast(_u8L("High")), + static_cast(_u8L("Medium")), + static_cast(_u8L("Low")), + static_cast(_u8L("Extra low")) }; ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); @@ -157,14 +156,13 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(!m_configuration.use_count); ImGui::Text(_L("Ratio").c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); - int wanted_count = m_configuration.wanted_count; ImGui::SetNextItemWidth(m_gui_cfg->input_width); const char * format = (m_configuration.wanted_percent > 10)? "%.0f %%": ((m_configuration.wanted_percent > 1)? "%.1f %%":"%.2f %%"); if (ImGui::SliderFloat("##triangle_ratio", &m_configuration.wanted_percent, 0.f, 100.f, format)) { m_is_valid_result = false; if (m_configuration.wanted_percent < 0.f) - m_configuration.wanted_percent = 0.01; + m_configuration.wanted_percent = 0.01f; if (m_configuration.wanted_percent > 100.f) m_configuration.wanted_percent = 100.f; m_configuration.update_percent(triangle_count); From 19f88d015d6e7ad78f3709b5eb6173ca01c7002c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 08:25:04 +0200 Subject: [PATCH 206/240] Follow-up of ab6c638e44915138ee0e0500931aefd63bba16ed -> Fixed logic to center objects loaded from 3mf files produced by 3rd part softwares --- src/slic3r/GUI/Plater.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 97ffc6cb7e..5f15635260 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1643,7 +1643,7 @@ struct Plater::priv BoundingBox scaled_bed_shape_bb() const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config, bool used_inches = false); - std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool force_center_on_bed = false); + std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false); wxString get_export_file(GUI::FileType file_type); @@ -2426,7 +2426,9 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (one_by_one) { - auto loaded_idxs = load_model_objects(model.objects, is_project_file, !is_project_file); + if (type_3mf && !is_project_file) + model.center_instances_around_point(bed_shape_bb().center()); + auto loaded_idxs = load_model_objects(model.objects, is_project_file); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } else { // This must be an .stl or .obj file, which may contain a maximum of one volume. @@ -2479,7 +2481,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ // #define AUTOPLACEMENT_ON_LOAD -std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool force_center_on_bed) +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z) { const BoundingBoxf bed_shape = bed_shape_bb(); const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); @@ -2539,9 +2541,6 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode object->ensure_on_bed(allow_negative_z); } - if (force_center_on_bed) - model.center_instances_around_point(bed_shape.center()); - #ifdef AUTOPLACEMENT_ON_LOAD // FIXME distance should be a config value ///////////////////////////////// auto min_obj_distance = static_cast(6/SCALING_FACTOR); From fefc20dbbb7919e8717be43c53202ae9addcdd68 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 24 Aug 2021 09:34:25 +0200 Subject: [PATCH 207/240] Slight refactoring of the project state. --- src/libslic3r/Preset.hpp | 4 ++-- src/slic3r/GUI/Plater.cpp | 9 +++------ src/slic3r/GUI/ProjectDirtyStateManager.cpp | 9 ++++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index d0e7561dbe..d19cb0aacc 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -371,7 +371,7 @@ public: const Preset& get_edited_preset() const { return m_edited_preset; } // Return the last saved preset. - const Preset& get_saved_preset() const { return m_saved_preset; } +// const Preset& get_saved_preset() const { return m_saved_preset; } // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist. PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const; @@ -474,7 +474,7 @@ public: // Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ. bool saved_is_dirty() const - { return is_dirty(&this->get_edited_preset(), &this->get_saved_preset()); } + { return is_dirty(&this->get_edited_preset(), &m_saved_preset); } // Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ. // std::vector saved_dirty_options() const // { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5f15635260..fa53522d10 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4703,10 +4703,8 @@ void Plater::load_project(const wxString& filename) std::vector input_paths; input_paths.push_back(into_path(filename)); - std::vector res = load_files(input_paths); - - // if res is empty no data has been loaded - if (!res.empty()) { + if (! load_files(input_paths).empty()) { + // At least one file was loaded. p->set_project_filename(filename); reset_project_dirty_initial_presets(); update_project_dirty_from_presets(); @@ -4741,8 +4739,7 @@ void Plater::add_model(bool imperial_units/* = false*/) } Plater::TakeSnapshot snapshot(this, snapshot_label); - std::vector res = load_files(paths, true, false, imperial_units); - if (!res.empty()) + if (! load_files(paths, true, false, imperial_units).empty()) wxGetApp().mainframe->update_title(); } diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.cpp b/src/slic3r/GUI/ProjectDirtyStateManager.cpp index 9efc1dd90a..3ca764e913 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.cpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.cpp @@ -28,7 +28,7 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac const size_t active_snapshot_time = stack.active_snapshot_time(); const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time)); const int idx = it - snapshots.begin() - 1; - const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ? + const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && idx < int(snapshots.size()) - 1) ? &snapshots[idx] : nullptr; assert(ret != nullptr); @@ -195,8 +195,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type) void ProjectDirtyStateManager::update_from_presets() { m_state.presets = false; - std::vector> selected_presets = wxGetApp().get_selected_presets(); - for (const auto& [type, name] : selected_presets) { + for (const auto& [type, name] : wxGetApp().get_selected_presets()) { m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name; } m_state.presets |= wxGetApp().has_unsaved_preset_changes(); @@ -214,6 +213,7 @@ void ProjectDirtyStateManager::reset_after_save() m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0; } else { + // Gizmo is active with its own Undo / Redo stack (for example the SLA support point editing gizmo). const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack); if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) { if (m_state.gizmos.current) @@ -231,8 +231,7 @@ void ProjectDirtyStateManager::reset_after_save() void ProjectDirtyStateManager::reset_initial_presets() { m_initial_presets = std::array(); - std::vector> selected_presets = wxGetApp().get_selected_presets(); - for (const auto& [type, name] : selected_presets) { + for (const auto& [type, name] : wxGetApp().get_selected_presets()) { m_initial_presets[type] = name; } } From 326fb51316f7a4311d46dd5c32f0e7245d0316bb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 24 Aug 2021 09:44:32 +0200 Subject: [PATCH 208/240] Force enable menu icons under GTK GTK specific: Fixed missing icons for wxMenuItem on GTK2 and GTK3 for skins that haven't enabled showing an icon for gtk_image_menu_item by forcing showing icons for every skin. --- src/slic3r/GUI/GUI_App.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 06d805eeb1..58ce12ae42 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -87,6 +87,11 @@ #include #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG +// Needed for forcing menu icons back under gtk2 and gtk3 +#if defined(__WXGTK20__) || defined(__WXGTK3__) + #include +#endif + namespace Slic3r { namespace GUI { @@ -799,6 +804,14 @@ bool GUI_App::OnInit() bool GUI_App::on_init_inner() { + // Forcing back menu icons under gtk2 and gtk3. Solution is based on: + // https://docs.gtk.org/gtk3/class.Settings.html + // see also https://docs.wxwidgets.org/3.0/classwx_menu_item.html#a2b5d6bcb820b992b1e4709facbf6d4fb + // TODO: Find workaround for GTK4 +#if defined(__WXGTK20__) || defined(__WXGTK3__) + g_object_set (gtk_settings_get_default (), "gtk-menu-images", TRUE, NULL); +#endif + // Verify resources path const wxString resources_dir = from_u8(Slic3r::resources_dir()); wxCHECK_MSG(wxDirExists(resources_dir), false, From 0b654e151852e049461d2da5a68056989c7d4835 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 11:23:33 +0200 Subject: [PATCH 209/240] Follow-up of 316d38807d64e377c4b1d3a71f8c9587dcc82066 -> Modified shader to work on (buggy) Intel graphics card --- resources/shaders/printbed.vs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs index ee19098c1c..7633017f12 100644 --- a/resources/shaders/printbed.vs +++ b/resources/shaders/printbed.vs @@ -7,6 +7,8 @@ varying vec2 tex_coords; void main() { - gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0); + gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0); + // the following line leads to crash on some Intel graphics card + //gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0); tex_coords = v_tex_coords; } From 6b03b66167dd17bc6c804f58b9af124082e80287 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 24 Aug 2021 11:24:48 +0200 Subject: [PATCH 210/240] OctoPrint upload: ignore certificate revocation checks Fixes Hostname incorrect While test is positive (Bonjour discovery not supported for the address field) #1781 (partial fix of #1781) --- src/libslic3r/Preset.cpp | 3 ++- src/libslic3r/PrintConfig.cpp | 6 ++++++ src/slic3r/GUI/PhysicalPrinterDialog.cpp | 9 ++++++++- src/slic3r/Utils/Http.cpp | 8 ++++++++ src/slic3r/Utils/Http.hpp | 2 ++ src/slic3r/Utils/OctoPrint.cpp | 9 ++++++++- src/slic3r/Utils/OctoPrint.hpp | 1 + 7 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c0a956154d..26359b866c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1442,7 +1442,8 @@ const std::vector& PhysicalPrinter::printer_options() "printhost_authorization_type", // HTTP digest authentization (RFC 2617) "printhost_user", - "printhost_password" + "printhost_password", + "printhost_ignore_check" }; } return s_opts; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e64824d0db..dffb8ac0c2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -310,6 +310,12 @@ void PrintConfigDef::init_common_params() // def->tooltip = L(""); def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + + def = this->add("printhost_ignore_check", coBool); + def->label = L("Ignore certificate revocation checks"); + // def->tooltip = L(""); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("preset_names", coStrings); def->label = L("Printer preset names"); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index f189378a6d..9c570172c0 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -396,6 +396,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_line(cafile_hint); } else { + Line line{ "", "" }; line.full_width = 1; @@ -411,8 +412,14 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr sizer->Add(txt, 1, wxEXPAND); return sizer; }; - m_optgroup->append_line(line); + +#ifdef WIN32 + option = m_optgroup->get_option("printhost_ignore_check"); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); +#endif + } for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) { diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 2afb7ed504..69093988ab 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -491,6 +491,14 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s return *this; } +Http& Http::revoke_best_effort(bool set) +{ + if(p && set){ + ::curl_easy_setopt(p->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT); + } + return *this; +} + Http& Http::set_post_body(const fs::path &path) { if (p) { p->set_post_body(path);} diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index f34a27fbcc..3cce250862 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -80,6 +80,8 @@ public: // Same as above except also override the file's filename with a custom one Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename); + Http& revoke_best_effort(bool set); + // Set the file contents as a POST request body. // The data is used verbatim, it is not additionally encoded in any way. // This can be used for hosts which do not support multipart requests. diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index ee669c36fe..3a2335b1ef 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -25,7 +25,8 @@ namespace Slic3r { OctoPrint::OctoPrint(DynamicPrintConfig *config) : host(config->opt_string("print_host")), apikey(config->opt_string("printhost_apikey")), - cafile(config->opt_string("printhost_cafile")) + cafile(config->opt_string("printhost_cafile")), + ignore_checks(config->opt_bool("printhost_ignore_check")) {} const char* OctoPrint::get_name() const { return "OctoPrint"; } @@ -73,6 +74,9 @@ bool OctoPrint::test(wxString &msg) const msg = "Could not parse server response"; } }) +#ifdef WIN32 + .revoke_best_effort(ignore_checks) +#endif .perform_sync(); return res; @@ -137,6 +141,9 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro res = false; } }) +#ifdef WIN32 + .revoke_best_effort(ignore_checks) +#endif .perform_sync(); return res; diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 62bdfb6fa1..42683dc62b 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -40,6 +40,7 @@ private: std::string host; std::string apikey; std::string cafile; + bool ignore_checks; virtual void set_auth(Http &http) const; std::string make_url(const std::string &path) const; From c5cc4ce2215846feaadec605e9052e8a0b10a660 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 11:31:04 +0200 Subject: [PATCH 211/240] Fixed sequential print clearance contours not disappearing after undo --- src/slic3r/GUI/Plater.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa53522d10..3884fbca4d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2944,6 +2944,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (view3D->is_layers_editing_enabled()) view3D->get_wxglcanvas()->Refresh(); + if (background_process.empty()) + view3D->get_canvas3d()->reset_sequential_print_clearance(); + if (invalidated == Print::APPLY_STATUS_INVALIDATED) { // Some previously calculated data on the Print was invalidated. // Hide the slicing results, as the current slicing status is no more valid. From d4584b11581295f26f7a83e3c71e700c70a1d6a9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 12:09:30 +0200 Subject: [PATCH 212/240] Removed confirmation dialog for command Delete All --- src/slic3r/GUI/Plater.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3884fbca4d..60215484b8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1658,6 +1658,7 @@ struct Plater::priv void deselect_all(); void remove(size_t obj_idx); void delete_object_from_model(size_t obj_idx); + void delete_all_objects_from_model(); void reset(); void mirror(Axis axis); void split_object(); @@ -1944,7 +1945,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); }); - view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); }); + view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { delete_all_objects_from_model(); }); +// view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); }); @@ -2753,6 +2755,32 @@ void Plater::priv::delete_object_from_model(size_t obj_idx) object_list_changed(); } +void Plater::priv::delete_all_objects_from_model() +{ + Plater::TakeSnapshot snapshot(q, _L("Delete All Objects")); + + if (view3D->is_layers_editing_enabled()) + view3D->enable_layers_editing(false); + + reset_gcode_toolpaths(); + gcode_result.reset(); + + view3D->get_canvas3d()->reset_sequential_print_clearance(); + + // Stop and reset the Print content. + background_process.reset(); + model.clear_objects(); + update(); + // Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model. + sidebar->obj_list()->delete_all_objects_from_list(); + object_list_changed(); + + // The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here + sidebar->show_sliced_info_sizer(false); + + model.custom_gcode_per_print_z.gcodes.clear(); +} + void Plater::priv::reset() { Plater::TakeSnapshot snapshot(q, _L("Reset Project")); From dfdfb32648d3d6b71c2e5032e68d3e57356a29dc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 12:17:47 +0200 Subject: [PATCH 213/240] Fixed update of project dirty state after discarding config changes --- src/libslic3r/Preset.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index d19cb0aacc..849dd7b809 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -395,7 +395,7 @@ public: void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; - update_saved_preset_from_current_preset(); +// update_saved_preset_from_current_preset(); } // Return a preset by its name. If the preset is active, a temporary copy is returned. From b6deda3477a17e27200432dd0a277d118835292f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 24 Aug 2021 12:39:21 +0200 Subject: [PATCH 214/240] Fixed object below bed after deleting part --- src/slic3r/GUI/Selection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index acb1f11131..ba4577bfb0 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1149,6 +1149,7 @@ void Selection::erase() } wxGetApp().obj_list()->delete_from_model_and_list(items); + ensure_not_below_bed(); } } From 16562a2e77ad74c44e8b143ae3e45deafa0912ee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 23 Aug 2021 15:51:06 +0200 Subject: [PATCH 215/240] Do not print brim when object has raft, it is not supported and makes no sense --- src/libslic3r/Brim.cpp | 6 +++++- src/libslic3r/Print.hpp | 8 ++++++-- src/libslic3r/PrintConfig.cpp | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index e73bed2c9f..f4455fdd55 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -71,12 +71,16 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print, Polygons islands; ConstPrintObjectPtrs island_to_object; for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) { + const PrintObject *object = print.objects()[print_object_idx]; + + if (! object->has_brim()) + continue; + Polygons islands_object; islands_object.reserve(bottom_layers_expolygons[print_object_idx].size()); for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) islands_object.emplace_back(ex_poly.contour); - const PrintObject *object = print.objects()[print_object_idx]; islands.reserve(islands.size() + object->instances().size() * islands_object.size()); for (const PrintInstance &instance : object->instances()) for (Polygon &poly : islands_object) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 491960705c..0056aee333 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -271,7 +271,11 @@ public: // Centering offset of the sliced mesh from the scaled and rotated mesh of the model. const Point& center_offset() const { return m_center_offset; } - bool has_brim() const { return this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.; } + bool has_brim() const { + return this->config().brim_type != btNoBrim + && this->config().brim_width.value > 0. + && ! this->has_raft(); + } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id @@ -321,7 +325,7 @@ public: bool has_raft() const { return m_config.raft_layers > 0; } bool has_support_material() const { return this->has_support() || this->has_raft(); } // Checks if the model object is painted using the multi-material painting gizmo. - bool is_mm_painted() const { return this->model_object()->is_mm_painted(); }; + bool is_mm_painted() const { return this->model_object()->is_mm_painted(); } // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) std::vector object_extruders() const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index dffb8ac0c2..c46ac04d69 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -471,7 +471,8 @@ void PrintConfigDef::init_fff_params() def = this->add("brim_width", coFloat); def->label = L("Brim width"); def->category = L("Skirt and brim"); - def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer."); + def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer." + "When raft is used, no brim is generated (use raft_first_layer_expansion)."); def->sidetext = L("mm"); def->min = 0; def->max = 200; From c029cd44a489f4f4a1abd822623fd0725c9a88b4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 24 Aug 2021 13:10:35 +0200 Subject: [PATCH 216/240] Initialization of various Preset list of config options was made thread safe. --- src/libslic3r/Preset.cpp | 403 ++++++++++++++++++--------------------- 1 file changed, 185 insertions(+), 218 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 26359b866c..bb083e03ce 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -413,212 +413,181 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) } } -const std::vector& Preset::print_options() -{ - static std::vector s_opts { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", - "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", - "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", - "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", - "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", - "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist", +static std::vector s_Preset_print_options { + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", + "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", + "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", + "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", + "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", + "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", + "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist", #ifdef HAS_PRESSURE_EQUALIZER - "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", + "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", #endif /* HAS_PRESSURE_EQUALIZER */ - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", - "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", - "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", - "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", - "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", - "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", - "support_material_contact_distance", "support_material_bottom_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", - "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", - "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" - }; - return s_opts; -} + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", + "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", + "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", + "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", + "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", + "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", + "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", + "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", + "support_material_contact_distance", "support_material_bottom_contact_distance", + "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", + "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", + "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", + "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", + "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", + "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", + "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" +}; -const std::vector& Preset::filament_options() -{ - static std::vector s_opts { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", - "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time", - "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", - "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", - "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", - "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", - "start_filament_gcode", "end_filament_gcode", - // Retract overrides - "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", - "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", - // Profile compatibility - "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" - }; - return s_opts; -} +static std::vector s_Preset_filament_options { + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", + "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time", + "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", + "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", + "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", + "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", + "start_filament_gcode", "end_filament_gcode", + // Retract overrides + "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", + "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", + // Profile compatibility + "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" +}; -const std::vector& Preset::machine_limits_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel", - "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", - "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", - "machine_min_extruding_rate", "machine_min_travel_rate", - "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", - }; - } - return s_opts; -} +static std::vector s_Preset_machine_limits_options { + "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel", + "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", + "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", + "machine_min_extruding_rate", "machine_min_travel_rate", + "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", +}; + +static std::vector s_Preset_printer_options { + "printer_technology", + "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", + "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. + "host_type", "print_host", "printhost_apikey", "printhost_cafile", + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "color_change_gcode", "pause_print_gcode", "template_custom_gcode", + "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", + "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height", + "default_print_profile", "inherits", + "remaining_times", "silent_mode", + "machine_limits_usage", "thumbnails" +}; + +static std::vector s_Preset_sla_print_options { + "layer_height", + "faded_layers", + "supports_enable", + "support_head_front_diameter", + "support_head_penetration", + "support_head_width", + "support_pillar_diameter", + "support_small_pillar_diameter_percent", + "support_max_bridges_on_pillar", + "support_pillar_connection_mode", + "support_buildplate_only", + "support_pillar_widening_factor", + "support_base_diameter", + "support_base_height", + "support_base_safety_distance", + "support_critical_angle", + "support_max_bridge_length", + "support_max_pillar_link_distance", + "support_object_elevation", + "support_points_density_relative", + "support_points_minimal_distance", + "slice_closing_radius", + "slicing_mode", + "pad_enable", + "pad_wall_thickness", + "pad_wall_height", + "pad_brim_size", + "pad_max_merge_distance", + // "pad_edge_radius", + "pad_wall_slope", + "pad_object_gap", + "pad_around_object", + "pad_around_object_everywhere", + "pad_object_connector_stride", + "pad_object_connector_width", + "pad_object_connector_penetration", + "hollowing_enable", + "hollowing_min_thickness", + "hollowing_quality", + "hollowing_closing_distance", + "output_filename_format", + "default_sla_print_profile", + "compatible_printers", + "compatible_printers_condition", + "inherits" +}; + +static std::vector s_Preset_sla_material_options { + "material_type", + "initial_layer_height", + "bottle_cost", + "bottle_volume", + "bottle_weight", + "material_density", + "exposure_time", + "initial_exposure_time", + "material_correction", + "material_notes", + "material_vendor", + "default_sla_material_profile", + "compatible_prints", "compatible_prints_condition", + "compatible_printers", "compatible_printers_condition", "inherits" +}; + +static std::vector s_Preset_sla_printer_options { + "printer_technology", + "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height", + "display_width", "display_height", "display_pixels_x", "display_pixels_y", + "display_mirror_x", "display_mirror_y", + "display_orientation", + "fast_tilt_time", "slow_tilt_time", "area_fill", + "relative_correction", + "absolute_correction", + "elefant_foot_compensation", + "elefant_foot_min_width", + "gamma_correction", + "min_exposure_time", "max_exposure_time", + "min_initial_exposure_time", "max_initial_exposure_time", + //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. + "print_host", "printhost_apikey", "printhost_cafile", + "printer_notes", + "inherits" +}; + +const std::vector& Preset::print_options() { return s_Preset_print_options; } +const std::vector& Preset::filament_options() { return s_Preset_filament_options; } +const std::vector& Preset::machine_limits_options() { return s_Preset_machine_limits_options; } +// The following nozzle options of a printer profile will be adjusted to match the size +// of the nozzle_diameter vector. +const std::vector& Preset::nozzle_options() { return print_config_def.extruder_option_keys(); } +const std::vector& Preset::sla_print_options() { return s_Preset_sla_print_options; } +const std::vector& Preset::sla_material_options() { return s_Preset_sla_material_options; } +const std::vector& Preset::sla_printer_options() { return s_Preset_sla_printer_options; } const std::vector& Preset::printer_options() { - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "printer_technology", - "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", - "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. - "host_type", "print_host", "printhost_apikey", "printhost_cafile", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "color_change_gcode", "pause_print_gcode", "template_custom_gcode", - "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", - "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height", - "default_print_profile", "inherits", - "remaining_times", "silent_mode", - "machine_limits_usage", "thumbnails" - }; - s_opts.insert(s_opts.end(), Preset::machine_limits_options().begin(), Preset::machine_limits_options().end()); - s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); - } - return s_opts; -} - -// The following nozzle options of a printer profile will be adjusted to match the size -// of the nozzle_diameter vector. -const std::vector& Preset::nozzle_options() -{ - return print_config_def.extruder_option_keys(); -} - -const std::vector& Preset::sla_print_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "layer_height", - "faded_layers", - "supports_enable", - "support_head_front_diameter", - "support_head_penetration", - "support_head_width", - "support_pillar_diameter", - "support_small_pillar_diameter_percent", - "support_max_bridges_on_pillar", - "support_pillar_connection_mode", - "support_buildplate_only", - "support_pillar_widening_factor", - "support_base_diameter", - "support_base_height", - "support_base_safety_distance", - "support_critical_angle", - "support_max_bridge_length", - "support_max_pillar_link_distance", - "support_object_elevation", - "support_points_density_relative", - "support_points_minimal_distance", - "slice_closing_radius", - "slicing_mode", - "pad_enable", - "pad_wall_thickness", - "pad_wall_height", - "pad_brim_size", - "pad_max_merge_distance", - // "pad_edge_radius", - "pad_wall_slope", - "pad_object_gap", - "pad_around_object", - "pad_around_object_everywhere", - "pad_object_connector_stride", - "pad_object_connector_width", - "pad_object_connector_penetration", - "hollowing_enable", - "hollowing_min_thickness", - "hollowing_quality", - "hollowing_closing_distance", - "output_filename_format", - "default_sla_print_profile", - "compatible_printers", - "compatible_printers_condition", - "inherits" - }; - } - return s_opts; -} - -const std::vector& Preset::sla_material_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "material_type", - "initial_layer_height", - "bottle_cost", - "bottle_volume", - "bottle_weight", - "material_density", - "exposure_time", - "initial_exposure_time", - "material_correction", - "material_notes", - "material_vendor", - "default_sla_material_profile", - "compatible_prints", "compatible_prints_condition", - "compatible_printers", "compatible_printers_condition", "inherits" - }; - } - return s_opts; -} - -const std::vector& Preset::sla_printer_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "printer_technology", - "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height", - "display_width", "display_height", "display_pixels_x", "display_pixels_y", - "display_mirror_x", "display_mirror_y", - "display_orientation", - "fast_tilt_time", "slow_tilt_time", "area_fill", - "relative_correction", - "absolute_correction", - "elefant_foot_compensation", - "elefant_foot_min_width", - "gamma_correction", - "min_exposure_time", "max_exposure_time", - "min_initial_exposure_time", "max_initial_exposure_time", - //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. - "print_host", "printhost_apikey", "printhost_cafile", - "printer_notes", - "inherits" - }; - } + static std::vector s_opts = [](){ + std::vector opts = s_Preset_printer_options; + append(opts, s_Preset_machine_limits_options); + append(opts, Preset::nozzle_options()); + return opts; + }(); return s_opts; } @@ -1426,27 +1395,25 @@ std::string PhysicalPrinter::separator() return " * "; } +static std::vector s_PhysicalPrinter_opts { + "preset_name", // temporary option to compatibility with older Slicer + "preset_names", + "printer_technology", + "host_type", + "print_host", + "printhost_apikey", + "printhost_cafile", + "printhost_port", + "printhost_authorization_type", + // HTTP digest authentization (RFC 2617) + "printhost_user", + "printhost_password", + "printhost_ignore_check" +}; + const std::vector& PhysicalPrinter::printer_options() { - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "preset_name", // temporary option to compatibility with older Slicer - "preset_names", - "printer_technology", - "host_type", - "print_host", - "printhost_apikey", - "printhost_cafile", - "printhost_port", - "printhost_authorization_type", - // HTTP digest authentization (RFC 2617) - "printhost_user", - "printhost_password", - "printhost_ignore_check" - }; - } - return s_opts; + return s_PhysicalPrinter_opts; } static constexpr auto legacy_print_host_options = { From d3f11a6ab7a7c350439929e9ebf2b03ef4989477 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 24 Aug 2021 13:57:52 +0200 Subject: [PATCH 217/240] Follow-up to OctoPrint upload: ignore certificate revocation checks 6b03b66167dd17bc6c804f58b9af124082e80287 Renamed the new "printhost_ignore_check" option to "printhost_ssl_ignore_revoke" Improved the Physical Printers dialog in regard to the new option checkbox (added tooltip to the checkbox, moved it to the end of options). Disabled the host_xxx options at the command line interface, they no more work after these options were separated to Physical Printers profiles. Little refactoring of Http.cpp/hpp, OctoPrint.cpp/hpp Private local variables prefixed with m_, some renaming for clarity. --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 49 +++++++++++++++--------- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 13 +++---- src/slic3r/Utils/Http.cpp | 4 +- src/slic3r/Utils/Http.hpp | 6 ++- src/slic3r/Utils/OctoPrint.cpp | 48 +++++++++++------------ src/slic3r/Utils/OctoPrint.hpp | 26 ++++++------- 7 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index bb083e03ce..3883c4980b 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1408,7 +1408,7 @@ static std::vector s_PhysicalPrinter_opts { // HTTP digest authentization (RFC 2617) "printhost_user", "printhost_password", - "printhost_ignore_check" + "printhost_ssl_ignore_revoke" }; const std::vector& PhysicalPrinter::printer_options() diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c46ac04d69..f010bad399 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -232,6 +232,16 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("elefant_foot_compensation", coFloat); + def->label = L("Elephant foot compensation"); + def->category = L("Advanced"); + def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value " + "to compensate for the 1st layer squish aka an Elephant Foot effect."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.)); + def = this->add("thumbnails", coPoints); def->label = L("G-code thumbnails"); def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); @@ -264,6 +274,7 @@ void PrintConfigDef::init_common_params() "Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL " "in the following format: https://username:password@your-octopi-address/"); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); def = this->add("printhost_apikey", coString); @@ -271,6 +282,7 @@ void PrintConfigDef::init_common_params() def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " "the API Key or the password required for authentication."); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); def = this->add("printhost_port", coString); @@ -278,6 +290,7 @@ void PrintConfigDef::init_common_params() def->tooltip = L("Name of the printer"); def->gui_type = ConfigOptionDef::GUIType::select_open; def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); def = this->add("printhost_cafile", coString); @@ -285,36 +298,32 @@ void PrintConfigDef::init_common_params() def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " "If left blank, the default OS CA certificate repository is used."); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - def = this->add("elefant_foot_compensation", coFloat); - def->label = L("Elephant foot compensation"); - def->category = L("Advanced"); - def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value " - "to compensate for the 1st layer squish aka an Elephant Foot effect."); - def->sidetext = L("mm"); - def->min = 0; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0.)); - // Options used by physical printers def = this->add("printhost_user", coString); def->label = L("User"); // def->tooltip = L(""); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); def = this->add("printhost_password", coString); def->label = L("Password"); // def->tooltip = L(""); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - def = this->add("printhost_ignore_check", coBool); - def->label = L("Ignore certificate revocation checks"); - // def->tooltip = L(""); + // Only available on Windows. + def = this->add("printhost_ssl_ignore_revoke", coBool); + def->label = L("Ignore HTTPS certificate revocation checks"); + def->tooltip = L("Ignore HTTPS certificate revocation checks in case of missing or offline distribution points. " + "One may want to enable this option for self signed certificates if connection fails."); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionBool(false)); def = this->add("preset_names", coStrings); @@ -323,12 +332,6 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionStrings()); - // temporary workaround for compatibility with older Slicer - { - def = this->add("preset_name", coString); - def->set_default_value(new ConfigOptionString()); - } - def = this->add("printhost_authorization_type", coEnum); def->label = L("Authorization Type"); // def->tooltip = L(""); @@ -338,7 +341,14 @@ void PrintConfigDef::init_common_params() def->enum_labels.push_back(L("API key")); def->enum_labels.push_back(L("HTTP digest")); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionEnum(atKeyPassword)); + + // temporary workaround for compatibility with older Slicer + { + def = this->add("preset_name", coString); + def->set_default_value(new ConfigOptionString()); + } } void PrintConfigDef::init_fff_params() @@ -1816,6 +1826,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back("AstroBox"); def->enum_labels.push_back("Repetier"); def->mode = comAdvanced; + def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionEnum(htOctoPrint)); def = this->add("only_retract_when_crossing_perimeters", coBool); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 9c570172c0..519abab43f 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -413,13 +413,6 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr return sizer; }; m_optgroup->append_line(line); - -#ifdef WIN32 - option = m_optgroup->get_option("printhost_ignore_check"); - option.opt.width = Field::def_width_wider(); - m_optgroup->append_single_option_line(option); -#endif - } for (const std::string& opt_key : std::vector{ "printhost_user", "printhost_password" }) { @@ -428,6 +421,12 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr m_optgroup->append_single_option_line(option); } +#ifdef WIN32 + option = m_optgroup->get_option("printhost_ssl_ignore_revoke"); + option.opt.width = Field::def_width_wider(); + m_optgroup->append_single_option_line(option); +#endif + m_optgroup->activate(); Field* printhost_field = m_optgroup->get_field("print_host"); diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 69093988ab..06598f8f5d 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -491,7 +491,9 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s return *this; } -Http& Http::revoke_best_effort(bool set) +// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present. +// This option is only supported for Schannel (the native Windows SSL library). +Http& Http::ssl_revoke_best_effort(bool set) { if(p && set){ ::curl_easy_setopt(p->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT); diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 3cce250862..52e48a394c 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -80,7 +80,11 @@ public: // Same as above except also override the file's filename with a custom one Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename); - Http& revoke_best_effort(bool set); +#ifdef WIN32 + // Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present. + // This option is only supported for Schannel (the native Windows SSL library). + Http& ssl_revoke_best_effort(bool set); +#endif // WIN32 // Set the file contents as a POST request body. // The data is used verbatim, it is not additionally encoded in any way. diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 3a2335b1ef..f9ae4af5a1 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -23,10 +23,10 @@ namespace pt = boost::property_tree; namespace Slic3r { OctoPrint::OctoPrint(DynamicPrintConfig *config) : - host(config->opt_string("print_host")), - apikey(config->opt_string("printhost_apikey")), - cafile(config->opt_string("printhost_cafile")), - ignore_checks(config->opt_bool("printhost_ignore_check")) + m_host(config->opt_string("print_host")), + m_apikey(config->opt_string("printhost_apikey")), + m_cafile(config->opt_string("printhost_cafile")), + m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke")) {} const char* OctoPrint::get_name() const { return "OctoPrint"; } @@ -75,7 +75,7 @@ bool OctoPrint::test(wxString &msg) const } }) #ifdef WIN32 - .revoke_best_effort(ignore_checks) + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) #endif .perform_sync(); @@ -142,7 +142,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro } }) #ifdef WIN32 - .revoke_best_effort(ignore_checks) + .ssl_revoke_best_effort(m_ssl_revoke_best_effort) #endif .perform_sync(); @@ -156,31 +156,31 @@ bool OctoPrint::validate_version_text(const boost::optional &versio void OctoPrint::set_auth(Http &http) const { - http.header("X-Api-Key", apikey); + http.header("X-Api-Key", m_apikey); - if (! cafile.empty()) { - http.ca_file(cafile); + if (!m_cafile.empty()) { + http.ca_file(m_cafile); } } std::string OctoPrint::make_url(const std::string &path) const { - if (host.find("http://") == 0 || host.find("https://") == 0) { - if (host.back() == '/') { - return (boost::format("%1%%2%") % host % path).str(); + if (m_host.find("http://") == 0 || m_host.find("https://") == 0) { + if (m_host.back() == '/') { + return (boost::format("%1%%2%") % m_host % path).str(); } else { - return (boost::format("%1%/%2%") % host % path).str(); + return (boost::format("%1%/%2%") % m_host % path).str(); } } else { - return (boost::format("http://%1%/%2%") % host % path).str(); + return (boost::format("http://%1%/%2%") % m_host % path).str(); } } SL1Host::SL1Host(DynamicPrintConfig *config) : OctoPrint(config), - authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), - username(config->opt_string("printhost_user")), - password(config->opt_string("printhost_password")) + m_authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), + m_username(config->opt_string("printhost_user")), + m_password(config->opt_string("printhost_password")) { } @@ -206,12 +206,12 @@ bool SL1Host::validate_version_text(const boost::optional &version_ void SL1Host::set_auth(Http &http) const { - switch (authorization_type) { + switch (m_authorization_type) { case atKeyPassword: http.header("X-Api-Key", get_apikey()); break; case atUserPassword: - http.auth_digest(username, password); + http.auth_digest(m_username, m_password); break; } @@ -223,9 +223,9 @@ void SL1Host::set_auth(Http &http) const // PrusaLink PrusaLink::PrusaLink(DynamicPrintConfig* config) : OctoPrint(config), - authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), - username(config->opt_string("printhost_user")), - password(config->opt_string("printhost_password")) + m_authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), + m_username(config->opt_string("printhost_user")), + m_password(config->opt_string("printhost_password")) { } @@ -250,12 +250,12 @@ bool PrusaLink::validate_version_text(const boost::optional& versio void PrusaLink::set_auth(Http& http) const { - switch (authorization_type) { + switch (m_authorization_type) { case atKeyPassword: http.header("X-Api-Key", get_apikey()); break; case atUserPassword: - http.auth_digest(username, password); + http.auth_digest(m_username, m_password); break; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 42683dc62b..48035b7951 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -29,18 +29,18 @@ public: bool has_auto_discovery() const override { return true; } bool can_test() const override { return true; } bool can_start_print() const override { return true; } - std::string get_host() const override { return host; } - const std::string& get_apikey() const { return apikey; } - const std::string& get_cafile() const { return cafile; } + std::string get_host() const override { return m_host; } + const std::string& get_apikey() const { return m_apikey; } + const std::string& get_cafile() const { return m_cafile; } protected: virtual bool validate_version_text(const boost::optional &version_text) const; private: - std::string host; - std::string apikey; - std::string cafile; - bool ignore_checks; + std::string m_host; + std::string m_apikey; + std::string m_cafile; + bool m_ssl_revoke_best_effort; virtual void set_auth(Http &http) const; std::string make_url(const std::string &path) const; @@ -65,10 +65,10 @@ private: void set_auth(Http &http) const override; // Host authorization type. - AuthorizationType authorization_type; + AuthorizationType m_authorization_type; // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string username; - std::string password; + std::string m_username; + std::string m_password; }; class PrusaLink : public OctoPrint @@ -90,10 +90,10 @@ private: void set_auth(Http& http) const override; // Host authorization type. - AuthorizationType authorization_type; + AuthorizationType m_authorization_type; // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string username; - std::string password; + std::string m_username; + std::string m_password; }; } From cfcfbc38d239e79d09b27f74a82bc254da249040 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 24 Aug 2021 16:48:05 +0200 Subject: [PATCH 218/240] Hints notification random weighted order with saving used hints to cache/hints.cereal --- src/libslic3r/AppConfig.cpp | 3 - src/slic3r/GUI/HintNotification.cpp | 224 ++++++++++++++++++++-------- src/slic3r/GUI/HintNotification.hpp | 21 ++- 3 files changed, 180 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 4166658f5a..177d8d708b 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -167,9 +167,6 @@ void AppConfig::set_defaults() if (get("show_splash_screen").empty()) set("show_splash_screen", "1"); - if (get("last_hint").empty()) - set("last_hint", "0"); - if (get("show_hints").empty()) set("show_hints", "1"); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 42039fa701..cd92cf4801 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -14,6 +14,31 @@ #include #include #include +#include +#include +#include + +#define HINTS_CEREAL_VERSION 1 +// structure for writing used hints into binary file with version +struct CerealData +{ + std::vector my_data; + // cereal will supply the version automatically when loading or saving + // The version number comes from the CEREAL_CLASS_VERSION macro + template + void serialize(Archive& ar, std::uint32_t const version) + { + // You can choose different behaviors depending on the version + // This is useful if you need to support older variants of your codebase + // interacting with newer ones + if (version > HINTS_CEREAL_VERSION) + throw Slic3r::IOError("Version of hints.cereal is higher than current version."); + else + ar(my_data); + } +}; +// version of used hints binary file +CEREAL_CLASS_VERSION(CerealData, HINTS_CEREAL_VERSION); namespace Slic3r { namespace GUI { @@ -31,6 +56,41 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f else ImGui::PushStyleColor(idx, col); } + + + + + +void write_used_binary(const std::vector& ids) +{ + boost::filesystem::ofstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"), std::ios::binary); + cereal::BinaryOutputArchive archive(file); + CerealData cd { ids }; + try + { + archive(cd); + } + catch (const std::exception& ex) + { + BOOST_LOG_TRIVIAL(error) << "Failed to write to hints.cereal. " << ex.what(); + } +} +void read_used_binary(std::vector& ids) +{ + boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal")); + cereal::BinaryInputArchive archive(file); + CerealData cd; + try + { + archive(cd); + } + catch (const std::exception& ex) + { + BOOST_LOG_TRIVIAL(error) << "Failed to load to hints.cereal. " << ex.what(); + return; + } + ids = cd.my_data; +} enum TagCheckResult { TagCheckAffirmative, @@ -179,30 +239,19 @@ void launch_browser_if_allowed(const std::string& url) if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") wxLaunchDefaultBrowser(url); } -bool pot_exists() -{ - return true; -// return boost::filesystem::exists(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.pot")); -} -void write_pot(const std::vector& elements) -{ - boost::filesystem::ofstream file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.pot")); - - for ( const auto &element : elements) - { - file << "msgid \"" << escape_string_cstyle(element) << "\"\nmsgstr \"\"\n\n"; - } - file.close(); -} } //namespace - +HintDatabase::~HintDatabase() +{ + if (m_initialized) { + write_used_binary(m_used_ids); + } +} void HintDatabase::init() { load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini")); const AppConfig* app_config = wxGetApp().app_config; - m_hint_id = std::atoi(app_config->get("last_hint").c_str()); m_initialized = true; } @@ -210,8 +259,6 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) { namespace pt = boost::property_tree; pt::ptree tree; - bool create_pot = !pot_exists(); - std::vector pot_elements; boost::nowide::ifstream ifs(path.string()); try { pt::read_ini(ifs, tree); @@ -227,19 +274,24 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) for (const auto& data : section.second) { dict.emplace(data.first, data.second.data()); } - - //unescaping and translating all texts and saving all data common for all hint types + // unique id string [hint:id] (trim "hint:") + std::string id_string = section.first.substr(5); + id_string = std::to_string(std::hash{}(id_string)); + // unescaping and translating all texts and saving all data common for all hint types std::string fulltext; std::string text1; std::string hypertext_text; std::string follow_text; + // tags std::string disabled_tags; std::string enabled_tags; + // optional link to documentation (accessed from button) std::string documentation_link; + // randomized weighted order variables + size_t weight = 1; + bool was_displayed = is_used(id_string); //unescape text1 unescape_string_cstyle(_utf8(dict["text"]), fulltext); - if (create_pot) - pot_elements.emplace_back(fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); std::string marker_e(1, ImGui::ColorMarkerEnd); @@ -295,37 +347,41 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) documentation_link = dict["documentation_link"]; } + if (dict.find("weight") != dict.end()) { + weight = (size_t)std::max(1, std::atoi(dict["weight"].c_str())); + } + // create HintData if (dict.find("hypertext_type") != dict.end()) { //link to internet if(dict["hypertext_type"] == "link") { std::string hypertext_link = dict["hypertext_link"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } }; m_loaded_hints.emplace_back(hint_data); // highlight settings } else if (dict["hypertext_type"] == "settings") { std::string opt = dict["hypertext_settings_opt"]; Preset::Type type = static_cast(std::atoi(dict["hypertext_settings_type"].c_str())); std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; m_loaded_hints.emplace_back(hint_data); // open preferences } else if(dict["hypertext_type"] == "preferences") { int page = static_cast(std::atoi(dict["hypertext_preferences_page"].c_str())); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "plater") { std::string item = dict["hypertext_plater_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gizmo") { std::string item = dict["hypertext_gizmo_item"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; m_loaded_hints.emplace_back(hint_data); } else if (dict["hypertext_type"] == "gallery") { - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { // Deselect all objects, otherwise gallery wont show. wxGetApp().plater()->canvas3D()->deselect_all(); wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; @@ -333,19 +389,17 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) } } else { // plain text without hypertext - HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link }; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link }; m_loaded_hints.emplace_back(hint_data); } } } - if (create_pot) - write_pot(pot_elements); } -HintData* HintDatabase::get_hint(bool up) +HintData* HintDatabase::get_hint(bool new_hint/* = true*/) { if (! m_initialized) { init(); - //return false; + new_hint = true; } if (m_loaded_hints.empty()) { @@ -353,23 +407,84 @@ HintData* HintDatabase::get_hint(bool up) return nullptr; } - // shift id - m_hint_id = (up ? m_hint_id + 1 : m_hint_id ); - m_hint_id %= m_loaded_hints.size(); + if (new_hint) + m_hint_id = get_next(); - AppConfig* app_config = wxGetApp().app_config; - app_config->set("last_hint", std::to_string(m_hint_id)); - - //data = &m_loaded_hints[m_hint_id]; - /* - data.text = m_loaded_hints[m_hint_id].text; - data.hypertext = m_loaded_hints[m_hint_id].hypertext; - data.follow_text = m_loaded_hints[m_hint_id].follow_text; - data.callback = m_loaded_hints[m_hint_id].callback; - */ return &m_loaded_hints[m_hint_id]; } +size_t HintDatabase::get_next() +{ + if (!m_sorted_hints) + { + auto compare_wieght = [](const HintData& a, const HintData& b){ return a.weight < b.weight; }; + std::sort(m_loaded_hints.begin(), m_loaded_hints.end(), compare_wieght); + m_sorted_hints = true; + srand(time(NULL)); + } + std::vector candidates; // index in m_loaded_hints + // total weight + size_t total_weight = 0; + for (size_t i = 0; i < m_loaded_hints.size(); i++) { + if (!m_loaded_hints[i].was_displayed && tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) { + candidates.emplace_back(i); + total_weight += m_loaded_hints[i].weight; + } + } + // all were shown + if (total_weight == 0) { + clear_used(); + for (size_t i = 0; i < m_loaded_hints.size(); i++) { + m_loaded_hints[i].was_displayed = false; + if (tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) { + candidates.emplace_back(i); + total_weight += m_loaded_hints[i].weight; + } + } + } + size_t random_number = rand() % total_weight + 1; + size_t current_weight = 0; + for (size_t i = 0; i < candidates.size(); i++) { + current_weight += m_loaded_hints[candidates[i]].weight; + if (random_number <= current_weight) { + set_used(m_loaded_hints[candidates[i]].id_string); + m_loaded_hints[candidates[i]].was_displayed = true; + return candidates[i]; + } + } + BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed."; + return 0; +} + +bool HintDatabase::is_used(const std::string& id) +{ + // load used ids from file + if (!m_used_ids_loaded) { + read_used_binary(m_used_ids); + m_used_ids_loaded = true; + } + // check if id is in used + for (const std::string& used_id : m_used_ids) { + if (used_id == id) + { + return true; + } + } + return false; +} +void HintDatabase::set_used(const std::string& id) +{ + // check needed? + if (!is_used(id)) + { + m_used_ids.emplace_back(id); + } +} +void HintDatabase::clear_used() +{ + m_used_ids.clear(); +} + void NotificationManager::HintNotification::count_spaces() { //determine line width @@ -865,23 +980,12 @@ void NotificationManager::HintNotification::open_documentation() launch_browser_if_allowed(m_documentation_link); } } -void NotificationManager::HintNotification::retrieve_data(int recursion_counter) +void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true*/) { - HintData* hint_data = HintDatabase::get_instance().get_hint(recursion_counter >= 0 ? true : false); + HintData* hint_data = HintDatabase::get_instance().get_hint(new_hint); if (hint_data == nullptr) close(); - if (hint_data != nullptr && !tags_check(hint_data->disabled_tags, hint_data->enabled_tags)) - { - // Content for different user - retrieve another - size_t count = HintDatabase::get_instance().get_count(); - if ((int)count < recursion_counter) { - BOOST_LOG_TRIVIAL(error) << "Hint notification failed to load data due to recursion counter."; - } else { - retrieve_data(recursion_counter + 1); - } - return; - } if(hint_data != nullptr) { NotificationData nd { NotificationType::DidYouKnowHint, diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index 78e02a848c..c1e7c0ed3c 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -9,7 +9,10 @@ namespace GUI { // Database of hints updatable struct HintData { + std::string id_string; std::string text; + size_t weight; + bool was_displayed; std::string hypertext; std::string follow_text; std::string disabled_tags; @@ -33,11 +36,12 @@ private: : m_hint_id(0) {} public: + ~HintDatabase(); HintDatabase(HintDatabase const&) = delete; void operator=(HintDatabase const&) = delete; // return true if HintData filled; - HintData* get_hint(bool up = true); + HintData* get_hint(bool new_hint = true); size_t get_count() { if (!m_initialized) return 0; @@ -46,10 +50,17 @@ public: private: void init(); void load_hints_from_file(const boost::filesystem::path& path); + bool is_used(const std::string& id); + void set_used(const std::string& id); + void clear_used(); + // Returns position in m_loaded_hints with next hint chosed randomly with weights + size_t get_next(); size_t m_hint_id; bool m_initialized { false }; std::vector m_loaded_hints; - + bool m_sorted_hints { false }; + std::vector m_used_ids; + bool m_used_ids_loaded { false }; }; // Notification class - shows current Hint ("Did you know") class NotificationManager::HintNotification : public NotificationManager::PopNotification @@ -58,10 +69,10 @@ public: HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool new_hint) : PopNotification(n, id_provider, evt_handler) { - retrieve_data(new_hint ? 0 : -1); + retrieve_data(new_hint); } virtual void init() override; - void open_next() { retrieve_data(0); } + void open_next() { retrieve_data(); } protected: virtual void set_next_window_size(ImGuiWrapper& imgui) override; virtual void count_spaces() override; @@ -87,7 +98,7 @@ protected: const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); // recursion counter -1 tells to retrieve same hint as last time - void retrieve_data(int recursion_counter = 0); + void retrieve_data(bool new_hint = true); void open_documentation(); bool m_has_hint_data { false }; From 2680d420558d312a361164345e691f45d9e8d82a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 24 Aug 2021 17:09:49 +0200 Subject: [PATCH 219/240] comments in hints.ini --- resources/data/hints.ini | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 28a13236d6..95060f9ec6 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -49,7 +49,13 @@ # Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) # Algorithm shows hint only if not in all disabled tags. # if there are both disabled and preferred, only preferred that are not in disabled are valid. - +# +# +# Notifications shows in random order, already shown notifications are saved at cache/hints.cereal (as binary - human non-readable) +# You can affect random ordering by seting weigh +# weight = 5 +# Weight must be larger or equal to 1. Default weight is 1. +# Weight defines probability as weight : sum_of_all_weights. [hint:Fuzzy skin] text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model. From e65dc37401b29f06fa571de63ce4d47c450c26f1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 24 Aug 2021 17:23:58 +0200 Subject: [PATCH 220/240] Refactoring + throwing exception from random generator in hints --- src/slic3r/GUI/HintNotification.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index cd92cf4801..990e4d37a9 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -20,7 +20,7 @@ #define HINTS_CEREAL_VERSION 1 // structure for writing used hints into binary file with version -struct CerealData +struct HintsCerealData { std::vector my_data; // cereal will supply the version automatically when loading or saving @@ -38,7 +38,7 @@ struct CerealData } }; // version of used hints binary file -CEREAL_CLASS_VERSION(CerealData, HINTS_CEREAL_VERSION); +CEREAL_CLASS_VERSION(HintsCerealData, HINTS_CEREAL_VERSION); namespace Slic3r { namespace GUI { @@ -65,7 +65,7 @@ void write_used_binary(const std::vector& ids) { boost::filesystem::ofstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"), std::ios::binary); cereal::BinaryOutputArchive archive(file); - CerealData cd { ids }; + HintsCerealData cd { ids }; try { archive(cd); @@ -79,7 +79,7 @@ void read_used_binary(std::vector& ids) { boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal")); cereal::BinaryInputArchive archive(file); - CerealData cd; + HintsCerealData cd; try { archive(cd); @@ -407,8 +407,17 @@ HintData* HintDatabase::get_hint(bool new_hint/* = true*/) return nullptr; } - if (new_hint) - m_hint_id = get_next(); + try + { + if (new_hint) + m_hint_id = get_next(); + } + catch (const std::exception&) + { + return nullptr; + } + + return &m_loaded_hints[m_hint_id]; } @@ -442,6 +451,10 @@ size_t HintDatabase::get_next() } } } + if (total_weight == 0) { + BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed. No suitable hint was found."; + throw std::exception(); + } size_t random_number = rand() % total_weight + 1; size_t current_weight = 0; for (size_t i = 0; i < candidates.size(); i++) { @@ -453,7 +466,7 @@ size_t HintDatabase::get_next() } } BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed."; - return 0; + throw std::exception(); } bool HintDatabase::is_used(const std::string& id) From 1c72351ba0aaf78dba86e5aeefe6bae65cdeb324 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 24 Aug 2021 17:31:34 +0200 Subject: [PATCH 221/240] Erase updatedItemsInfo type from multiple-showing notifications list. --- src/slic3r/GUI/NotificationManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2bc9aaaabf..2be8ba26ba 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -538,7 +538,7 @@ private: // Timestamp of last rendering int64_t m_last_render { 0LL }; // Notification types that can be shown multiple types at once (compared by text) - const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload, NotificationType::UpdatedItemsInfo }; + const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload }; //prepared (basic) notifications static const NotificationData basic_notifications[]; }; From d701dfe436b353f4847b7a78605847f85e50e4eb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 25 Aug 2021 09:11:20 +0200 Subject: [PATCH 222/240] Fixed deletion of objects/volumes by pressing Del key while the mouse cursor is hovering the objects list in sidebar --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 0c555454d3..1e290beae8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event) || event.GetKeyCode() == WXK_BACK #endif //__WXOSX__ ) { - remove(); + wxGetApp().plater()->remove_selected(); } else if (event.GetKeyCode() == WXK_F5) wxGetApp().plater()->reload_all_from_disk(); From 783c9cf2028e994608100ba598fd206ceccec93b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 25 Aug 2021 09:34:32 +0200 Subject: [PATCH 223/240] Fix of d3f11a6ab7a7c350439929e9ebf2b03ef4989477 --- src/slic3r/Utils/Http.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 06598f8f5d..f1614017f0 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -491,6 +491,7 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s return *this; } +#ifdef WIN32 // Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present. // This option is only supported for Schannel (the native Windows SSL library). Http& Http::ssl_revoke_best_effort(bool set) @@ -500,6 +501,7 @@ Http& Http::ssl_revoke_best_effort(bool set) } return *this; } +#endif // WIN32 Http& Http::set_post_body(const fs::path &path) { From debf4981a6a20f67545c399b3144014fe40c2a10 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 09:44:50 +0200 Subject: [PATCH 224/240] Fix clang for VisualStudio usage --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 6b68254ec0..440c89ec57 100644 --- a/.clang-format +++ b/.clang-format @@ -77,7 +77,7 @@ IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true -KeepLineBreaksForNonEmptyLines: false +#KeepLineBreaksForNonEmptyLines: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' From 369f325f4ed396271401ea267e8327cd73c4a0cc Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Aug 2021 09:52:27 +0200 Subject: [PATCH 225/240] Delete unused variable --- src/slic3r/GUI/HintNotification.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 990e4d37a9..1d5931c486 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -248,12 +248,8 @@ HintDatabase::~HintDatabase() } void HintDatabase::init() { - load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini")); - - const AppConfig* app_config = wxGetApp().app_config; m_initialized = true; - } void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) { From beb38d09ae16c7d8308806be59893a7d0413548c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 10:51:11 +0200 Subject: [PATCH 226/240] Add reserve to mesh boolean operation --- src/libslic3r/MeshBoolean.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 3dd5da307f..a59165946a 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -118,6 +118,11 @@ void triangle_mesh_to_cgal(const std::vector & V, { if (F.empty()) return; + size_t vertices_count = V.size(); + size_t edges_count = (F.size()* 3) / 2; + size_t faces_count = F.size(); + out.reserve(vertices_count, edges_count, faces_count); + for (auto &v : V) out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()}); From 3fc4cc84a7b0a3a86b5162039e04d4cce079367c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 25 Aug 2021 12:01:41 +0200 Subject: [PATCH 227/240] Fixed invalidation when first_layer_height is changed --- src/libslic3r/Print.cpp | 3 ++- src/libslic3r/PrintObject.cpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b3a1bc9939..48737f8302 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -159,7 +159,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "wipe_tower_rotation_angle") { steps.emplace_back(psSkirtBrim); } else if ( - opt_key == "nozzle_diameter" + opt_key == "first_layer_height" + || opt_key == "nozzle_diameter" || opt_key == "resolution" // Spiral Vase forces different kind of slicing than the normal model: // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ae98165310..ee09e0f5b7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -535,7 +535,6 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posPerimeters); } else if ( opt_key == "layer_height" - || opt_key == "first_layer_height" || opt_key == "mmu_segmented_region_max_width" || opt_key == "raft_layers" || opt_key == "raft_contact_distance" From 38cd7fea6540ecf41cd0773f859861ef7924757e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 25 Aug 2021 12:39:46 +0200 Subject: [PATCH 228/240] Fixed update of backend after the changes introduced with b3010a817bdc8e63b2ad2f9d281be3ba89216ba7 (Do not allow objects to be placed fully below bed) and pop object on the bed when only one part is left --- src/libslic3r/Model.cpp | 53 +++++++++++++++++++++++++++++-- src/libslic3r/Model.hpp | 2 ++ src/slic3r/GUI/GUI_ObjectList.cpp | 11 +++---- src/slic3r/GUI/Plater.cpp | 3 +- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 235c357978..8eb5bb3890 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -956,9 +956,22 @@ void ModelObject::center_around_origin(bool include_modifiers) void ModelObject::ensure_on_bed(bool allow_negative_z) { - const double min_z = get_min_z(); - if (!allow_negative_z || min_z > SINKING_Z_THRESHOLD) - translate_instances({ 0.0, 0.0, -min_z }); + double z_offset = 0.0; + + if (allow_negative_z) { + if (volumes.size() == 1) + z_offset = -get_min_z(); + else { + const double max_z = get_max_z(); + if (max_z < SINKING_MIN_Z_THRESHOLD) + z_offset = SINKING_MIN_Z_THRESHOLD - max_z; + } + } + else + z_offset = -get_min_z(); + + if (z_offset != 0.0) + translate_instances(z_offset * Vec3d::UnitZ()); } void ModelObject::translate_instances(const Vec3d& vector) @@ -1429,6 +1442,19 @@ double ModelObject::get_min_z() const } } +double ModelObject::get_max_z() const +{ + if (instances.empty()) + return 0.0; + else { + double max_z = -DBL_MAX; + for (size_t i = 0; i < instances.size(); ++i) { + max_z = std::max(max_z, get_instance_max_z(i)); + } + return max_z; + } +} + double ModelObject::get_instance_min_z(size_t instance_idx) const { double min_z = DBL_MAX; @@ -1450,6 +1476,27 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const return min_z + inst->get_offset(Z); } +double ModelObject::get_instance_max_z(size_t instance_idx) const +{ + double max_z = -DBL_MAX; + + const ModelInstance* inst = instances[instance_idx]; + const Transform3d& mi = inst->get_matrix(true); + + for (const ModelVolume* v : volumes) { + if (!v->is_model_part()) + continue; + + const Transform3d mv = mi * v->get_matrix(); + const TriangleMesh& hull = v->get_convex_hull(); + for (const stl_facet& facet : hull.stl.facet_start) + for (int i = 0; i < 3; ++i) + max_z = std::max(max_z, (mv * facet.vertex[i].cast()).z()); + } + + return max_z + inst->get_offset(Z); +} + unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { unsigned int num_printable = 0; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 1dd16ee917..25f19e7928 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -358,7 +358,9 @@ public: void bake_xy_rotation_into_meshes(size_t instance_idx); double get_min_z() const; + double get_max_z() const; double get_instance_min_z(size_t instance_idx) const; + double get_instance_max_z(size_t instance_idx) const; // Called by Print::validate() from the UI thread. unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 1e290beae8..23ce819dd4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1920,16 +1920,15 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con if (vol->is_model_part()) ++solid_cnt; if (volume->is_model_part() && solid_cnt == 1) { - Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last solid part from object."))); + Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object.")); return false; } - take_snapshot(_(L("Delete Subobject"))); + take_snapshot(_L("Delete Subobject")); object->delete_volume(idx); - if (object->volumes.size() == 1) - { + if (object->volumes.size() == 1) { const auto last_volume = object->volumes[0]; if (!last_volume->config.empty()) { object->config.apply(last_volume->config); @@ -1948,11 +1947,11 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con } else if (type == itInstance) { if (object->instances.size() == 1) { - Slic3r::GUI::show_error(nullptr, _(L("Last instance of an object cannot be deleted."))); + Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted.")); return false; } - take_snapshot(_(L("Delete Instance"))); + take_snapshot(_L("Delete Instance")); object->delete_instance(idx); } else diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 60215484b8..72b2c5909d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6200,8 +6200,7 @@ void Plater::changed_object(int obj_idx) if (obj_idx < 0) return; // recenter and re - align to Z = 0 - auto model_object = p->model.objects[obj_idx]; - model_object->ensure_on_bed(this->p->printer_technology != ptSLA); + p->model.objects[obj_idx]->ensure_on_bed(p->printer_technology != ptSLA); if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data, update the 3D scene. From a918314aab574075e256ce30e1a1e1b29f287a54 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 25 Aug 2021 13:01:08 +0200 Subject: [PATCH 229/240] Fix UI glitches when "fill bed" fails to add any additional instances --- src/slic3r/GUI/Jobs/FillBedJob.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/Jobs/FillBedJob.cpp b/src/slic3r/GUI/Jobs/FillBedJob.cpp index 1948c8fbbf..870f31f2fc 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.cpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.cpp @@ -147,26 +147,28 @@ void FillBedJob::finalize() size_t inst_cnt = model_object->instances.size(); - for (ArrangePolygon &ap : m_selected) { - if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0)) - ap.apply(); - } + int added_cnt = std::accumulate(m_selected.begin(), m_selected.end(), 0, [](int s, auto &ap) { + return s + int(ap.priority == 0 && ap.bed_idx == 0); + }); - model_object->ensure_on_bed(); + if (added_cnt > 0) { + for (ArrangePolygon &ap : m_selected) { + if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0)) + ap.apply(); + } - m_plater->update(); + model_object->ensure_on_bed(); - int added_cnt = std::accumulate(m_selected.begin(), m_selected.end(), 0, - [](int s, auto &ap) { - return s + int(ap.priority == 0 && ap.bed_idx == 0); - }); + m_plater->update(); - // FIXME: somebody explain why this is needed for increase_object_instances - if (inst_cnt == 1) added_cnt++; + // FIXME: somebody explain why this is needed for increase_object_instances + if (inst_cnt == 1) added_cnt++; - if (added_cnt > 0) m_plater->sidebar() .obj_list()->increase_object_instances(m_object_idx, size_t(added_cnt)); + } + + Job::finalize(); } }} // namespace Slic3r::GUI From 9896a2190466d1c0d6a4c76e7ec0541f6bef3072 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 13:09:37 +0200 Subject: [PATCH 230/240] @Vojta request --> change ratio to decimation ratio --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 80 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 27 +++++--- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index f0336f729e..3530b4240e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -17,10 +17,17 @@ GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, : GLGizmoBase(parent, icon_filename, -1) , m_state(State::settings) , m_is_valid_result(false) + , m_exist_preview(false) , m_progress(0) , m_volume(nullptr) , m_obj_index(0) , m_need_reload(false) + + , tr_mesh_name(_u8L("Mesh name")) + , tr_triangles(_u8L("Triangles")) + , tr_preview(_u8L("Preview")) + , tr_detail_level(_u8L("Detail level")) + , tr_decimate_ratio(_u8L("Decimate ratio")) {} GLGizmoSimplify::~GLGizmoSimplify() { @@ -45,9 +52,7 @@ void GLGizmoSimplify::on_render_for_picking() {} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { - const int max_char_in_name = 20; create_gui_cfg(); - const Selection &selection = m_parent.get_selection(); int object_idx = selection.get_object_idx(); ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; @@ -65,15 +70,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_obj_index = object_idx; // to remember correct object m_volume = act_volume; m_original_its = {}; - const TriangleMesh &tm = m_volume->mesh(); - m_configuration.wanted_percent = 50.; // default value - m_configuration.update_percent(tm.its.indices.size()); + m_configuration.decimate_ratio = 50.; // default value + m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size()); m_is_valid_result = false; + m_exist_preview = false; if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); - pos.x -= m_gui_cfg->window_offset; - pos.y -= m_gui_cfg->window_offset; + pos.x -= m_gui_cfg->window_offset_x; + pos.y -= m_gui_cfg->window_offset_y; // minimal top left value ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding); if (pos.x < tl.x) pos.x = tl.x; @@ -81,8 +86,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // maximal bottom right value auto parent_size = m_parent.get_canvas_size(); ImVec2 br( - parent_size.get_width() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding), - parent_size.get_height() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding)); + parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding), + parent_size.get_height() - (2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding)); if (pos.x > br.x) pos.x = br.x; if (pos.y > br.y) pos.y = br.y; ImGui::SetNextWindowPos(pos, ImGuiCond_Always); @@ -98,15 +103,23 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_original_its.has_value()) triangle_count = m_original_its->indices.size(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":"); ImGui::SameLine(m_gui_cfg->top_left_width); std::string name = m_volume->name; - if (name.length() > max_char_in_name) - name = name.substr(0, max_char_in_name-3) + "..."; + if (name.length() > m_gui_cfg->max_char_in_name) + name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "..."; m_imgui->text(name); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":"); ImGui::SameLine(m_gui_cfg->top_left_width); m_imgui->text(std::to_string(triangle_count)); + /* + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_preview + ":"); + ImGui::SameLine(m_gui_cfg->top_left_width); + if (m_exist_preview) { + m_imgui->text(std::to_string(m_volume->mesh().its.indices.size())); + } else { + m_imgui->text("---"); + }*/ ImGui::Separator(); @@ -116,7 +129,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); m_imgui->disabled_begin(m_configuration.use_count); - ImGui::Text(_L("Detail level").c_str()); + ImGui::Text(tr_detail_level.c_str()); std::vector reduce_captions = { static_cast(_u8L("Extra high")), static_cast(_u8L("High")), @@ -150,22 +163,23 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // show preview result triangle count (percent) if (m_need_reload && !m_configuration.use_count) { m_configuration.wanted_count = static_cast(m_volume->mesh().its.indices.size()); - m_configuration.update_count(triangle_count); + m_configuration.decimate_ratio = + (1.0f - (m_configuration.wanted_count / (float) triangle_count)) * 100.f; } m_imgui->disabled_begin(!m_configuration.use_count); - ImGui::Text(_L("Ratio").c_str()); + ImGui::Text(tr_decimate_ratio.c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); - const char * format = (m_configuration.wanted_percent > 10)? "%.0f %%": - ((m_configuration.wanted_percent > 1)? "%.1f %%":"%.2f %%"); - if (ImGui::SliderFloat("##triangle_ratio", &m_configuration.wanted_percent, 0.f, 100.f, format)) { + const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%": + ((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%"); + if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) { m_is_valid_result = false; - if (m_configuration.wanted_percent < 0.f) - m_configuration.wanted_percent = 0.01f; - if (m_configuration.wanted_percent > 100.f) - m_configuration.wanted_percent = 100.f; - m_configuration.update_percent(triangle_count); + if (m_configuration.decimate_ratio < 0.f) + m_configuration.decimate_ratio = 0.01f; + if (m_configuration.decimate_ratio > 100.f) + m_configuration.decimate_ratio = 100.f; + m_configuration.fix_count_by_ratio(triangle_count); } ImGui::NewLine(); @@ -195,7 +209,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi process(); } else { // use preview and close - if (m_original_its.has_value()) { + if (m_exist_preview) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); plater->changed_mesh(m_obj_index); @@ -286,6 +300,7 @@ void GLGizmoSimplify::process() its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); m_is_valid_result = true; + m_exist_preview = true; } catch (SimplifyCanceledException &) { // set state out of main thread m_state = State::settings; @@ -326,7 +341,7 @@ void GLGizmoSimplify::on_set_state() } // revert preview - if (m_original_its.has_value()) { + if (m_exist_preview) { set_its(*m_original_its); m_parent.reload_scene(true); m_need_reload = false; @@ -344,18 +359,19 @@ void GLGizmoSimplify::create_gui_cfg() { if (m_gui_cfg.has_value()) return; int space_size = m_imgui->calc_text_size(":MM").x; GuiCfg cfg; - cfg.top_left_width = std::max(m_imgui->calc_text_size(_L("Mesh name")).x, - m_imgui->calc_text_size(_L("Triangles")).x) + cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x, + m_imgui->calc_text_size(tr_triangles).x) + space_size; const float radio_size = ImGui::GetFrameHeight(); cfg.bottom_left_width = - std::max(m_imgui->calc_text_size(_L("Detail level")).x, - m_imgui->calc_text_size(_L("Ratio")).x) + + std::max(m_imgui->calc_text_size(tr_detail_level).x, + m_imgui->calc_text_size(tr_decimate_ratio).x) + space_size + radio_size; - cfg.input_width = cfg.bottom_left_width; - cfg.window_offset = cfg.input_width; + cfg.input_width = cfg.bottom_left_width * 1.5; + cfg.window_offset_x = (cfg.bottom_left_width + cfg.input_width)/2; + cfg.window_offset_y = ImGui::GetTextLineHeightWithSpacing() * 5; m_gui_cfg = cfg; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 3d667d6917..9af6686bd1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -37,7 +37,9 @@ private: void create_gui_cfg(); void request_rerender(); - bool m_is_valid_result; // differ what to do in apply + std::atomic_bool m_is_valid_result; // differ what to do in apply + std::atomic_bool m_exist_preview; // set when process end + volatile int m_progress; // percent of done work ModelVolume *m_volume; // size_t m_obj_index; @@ -59,20 +61,16 @@ private: { bool use_count = false; // minimal triangle count - float wanted_percent = 50.f; + float decimate_ratio = 50.f; // in percent uint32_t wanted_count = 0; // initialize by percents // maximal quadric error float max_error = 1.; - void update_count(size_t triangle_count) - { - wanted_percent = (float) wanted_count / triangle_count * 100.f; - } - void update_percent(size_t triangle_count) + void fix_count_by_ratio(size_t triangle_count) { wanted_count = static_cast( - std::round(triangle_count * wanted_percent / 100.f)); + std::round(triangle_count * (100.f-decimate_ratio) / 100.f)); } } m_configuration; @@ -84,10 +82,21 @@ private: int top_left_width = 100; int bottom_left_width = 100; int input_width = 100; - int window_offset = 100; + int window_offset_x = 100; + int window_offset_y = 100; int window_padding = 0; + + // trunc model name when longer + int max_char_in_name = 30; }; std::optional m_gui_cfg; + + // translations used for calc window size + const std::string tr_mesh_name; + const std::string tr_triangles; + const std::string tr_preview; + const std::string tr_detail_level; + const std::string tr_decimate_ratio; }; } // namespace GUI From 0e4a987bed809a6933b89bfc400316440051580d Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 13:17:36 +0200 Subject: [PATCH 231/240] extend git ignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index efeaa91b5f..63004bab25 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ MANIFEST.bak xs/MANIFEST.bak xs/assertlib* .init_bundle.ini +.vs/* local-lib /src/TAGS /.vscode/ +build-linux/* +deps/build-linux/* From ca098d576313d0c20169d543ab1338e6150ddcac Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 25 Aug 2021 15:01:15 +0200 Subject: [PATCH 232/240] Fixed manipulation of sinking multipart objects resulting in wrong object/parts positioning --- src/libslic3r/Model.cpp | 11 ++++++++++- src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/Selection.cpp | 22 +++++++++++++++++----- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 8eb5bb3890..e32285106e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -959,7 +959,7 @@ void ModelObject::ensure_on_bed(bool allow_negative_z) double z_offset = 0.0; if (allow_negative_z) { - if (volumes.size() == 1) + if (parts_count() == 1) z_offset = -get_min_z(); else { const double max_z = get_max_z(); @@ -1127,6 +1127,15 @@ size_t ModelObject::facets_count() const return num; } +size_t ModelObject::parts_count() const +{ + size_t num = 0; + for (const ModelVolume* v : this->volumes) + if (v->is_model_part()) + ++num; + return num; +} + bool ModelObject::needed_repair() const { for (const ModelVolume *v : this->volumes) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 25f19e7928..b89dd5aa1e 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -347,6 +347,7 @@ public: size_t materials_count() const; size_t facets_count() const; + size_t parts_count() const; bool needed_repair() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index ba4577bfb0..3409c531da 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2154,11 +2154,23 @@ void Selection::ensure_not_below_bed() } } - for (GLVolume* volume : *m_volumes) { - std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); - InstancesToZMap::iterator it = instances_max_z.find(instance); - if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD) - volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second); + if (is_any_volume()) { + for (unsigned int i : m_list) { + GLVolume& volume = *(*m_volumes)[i]; + std::pair instance = std::make_pair(volume.object_idx(), volume.instance_idx()); + InstancesToZMap::iterator it = instances_max_z.find(instance); + double z_shift = SINKING_MIN_Z_THRESHOLD - it->second; + if (it != instances_max_z.end() && z_shift > 0.0) + volume.set_volume_offset(Z, volume.get_volume_offset(Z) + z_shift); + } + } + else { + for (GLVolume* volume : *m_volumes) { + std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::iterator it = instances_max_z.find(instance); + if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD) + volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second); + } } } From e6bae065571c0d8c95ec7ba6100a50fb094f7f80 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Aug 2021 15:07:30 +0200 Subject: [PATCH 233/240] Notice dialog when opening any link outside PrusaSlicer --- src/slic3r/GUI/AboutDialog.cpp | 4 ++-- src/slic3r/GUI/GUI_App.cpp | 19 ++++++++++++++++++- src/slic3r/GUI/GUI_App.hpp | 3 ++- src/slic3r/GUI/HintNotification.cpp | 3 +-- src/slic3r/GUI/MainFrame.cpp | 6 +++--- src/slic3r/GUI/NotificationManager.cpp | 2 +- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index fdbc1a6d3d..72f1421f68 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -195,7 +195,7 @@ void CopyrightsDialog::on_dpi_changed(const wxRect &suggested_rect) void CopyrightsDialog::onLinkClicked(wxHtmlLinkEvent &event) { - wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); + wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref()); event.Skip(false); } @@ -344,7 +344,7 @@ void AboutDialog::on_dpi_changed(const wxRect &suggested_rect) void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event) { - wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); + wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref()); event.Skip(false); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 58ce12ae42..102026a0cb 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2327,7 +2327,7 @@ wxString GUI_App::current_language_code_safe() const void GUI_App::open_web_page_localized(const std::string &http_address) { - wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe()); + open_browser_with_warning_dialog(http_address + "&lng=" + this->current_language_code_safe()); } bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page) @@ -2525,6 +2525,23 @@ void GUI_App::check_updates(const bool verbose) } } +bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/) +{ + bool launch = true; + + if (get_app_config()->get("suppress_hyperlinks").empty()) { + wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO); + dialog.ShowCheckBox(_L("Remember my choice")); + int answer = dialog.ShowModal(); + launch = answer == wxID_YES; + get_app_config()->set("suppress_hyperlinks", dialog.IsCheckBoxChecked() ? (answer == wxID_NO ? "1" : "0") : ""); + } + if (launch) + launch = get_app_config()->get("suppress_hyperlinks") != "1"; + + return launch && wxLaunchDefaultBrowser(url, flags); +} + // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index a140247e09..1d8c2fcf75 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -261,7 +261,8 @@ public: void open_preferences(size_t open_on_tab = 0); virtual bool OnExceptionInMainLoop() override; - + // Calls wxLaunchDefaultBrowser if user confirms in dialog. + bool open_browser_with_warning_dialog(const wxString& url, int flags = 0); #ifdef __APPLE__ void OSXStoreOpenFiles(const wxArrayString &files) override; // wxWidgets override to get an event on open files. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 1d5931c486..ead7a8f29a 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -236,8 +236,7 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag } void launch_browser_if_allowed(const std::string& url) { - if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") - wxLaunchDefaultBrowser(url); + wxGetApp().open_browser_with_warning_dialog(url); } } //namespace HintDatabase::~HintDatabase() diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 498b026056..329a9a62a7 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1057,7 +1057,7 @@ static wxMenu* generate_help_menu() append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"), [](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); }); append_menu_item(helpMenu, wxID_ANY, _L("Software &Releases"), _L("Open the software releases page in your browser"), - [](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); }); + [](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); }); //# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{ //# wxTheApp->check_version(1); //# }); @@ -1067,14 +1067,14 @@ static wxMenu* generate_help_menu() [](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); }); // append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Manual"), SLIC3R_APP_NAME), // wxString::Format(_L("Open the %s manual in your browser"), SLIC3R_APP_NAME), -// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); }); +// [this](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("http://manual.slic3r.org/"); }); helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("System &Info"), _L("Show system information"), [](wxCommandEvent&) { wxGetApp().system_info(); }); append_menu_item(helpMenu, wxID_ANY, _L("Show &Configuration Folder"), _L("Show user configuration folder (datadir)"), [](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); }); append_menu_item(helpMenu, wxID_ANY, _L("Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME), - [](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); }); + [](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/slic3r/issues/new"); }); if (wxGetApp().is_editor()) append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"), [](wxCommandEvent&) { Slic3r::GUI::about(); }); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 4c1c404dad..3278a5a6e3 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -42,7 +42,7 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat } }, {NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { - wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, + wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, {NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10, _u8L("You have just added a G-code for color change, but its value is empty.\n" "To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") }, From 02dd1b5f7d5cca8a9aafde182409261dc06f5cc3 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Aug 2021 16:31:23 +0200 Subject: [PATCH 234/240] rendering highlight arrow from svg --- resources/icons/toolbar_arrow.png | Bin 3996 -> 0 bytes resources/icons/toolbar_arrow.svg | 94 +++++----------------- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/GUI/GLToolbar.cpp | 24 +++--- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 5 +- 5 files changed, 36 insertions(+), 95 deletions(-) delete mode 100644 resources/icons/toolbar_arrow.png diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png deleted file mode 100644 index a370442ff0e933b85d436f3abac584b2e5ac3f37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3996 zcmeHK_cI&-)4qrn9EskEe$hM8+i6j94yQ&ZN{F5l5vPR^33Ac7aD;G3^p+e)ln}ig z=afW?UZcLg-@gCheP?#(+1+_|W@mqRb~e%6R3AviP6Yq}fQAOTkZTMF0B(>|l3zy{ zm4}npa3ct!uMNNqa;{w`q@G$PS^z*zD)qTD*>!#^z`!O50HE#sKfm$A|C8Hwku6vc z7Hr|~5gg_m=ne=A3zK;K#3#trIlx`QKhQI0OO^ea{nJoa3mWdYnRhF|u8ZwQSb^Az zda&S)G#G>=dPLhUUVDv9ycUht)FFw^Xo-c@-{p)0O*U?7uDvw`;AMbh@f`G&XmPbw zt&-qE9aJrixsz{yC?tCdnE^1gH-_W=58H=0m1@xM4-S5x0T(x;ZoWa!N1PqCHMFE5 zq#_Sx&5mG%P?15zhxb)uBGMva|5`PaBHDc&k8ez*#TGl&nI{{^bySIhiXtLnc07%y zC-yWWR^(xNw1EiA{auFii@6I(PYtXt40>5m8ncUAgXtt)Dcb28&>jS*z%b;YPeEOe z*GhVTFdZt0{*LCwY-4TUA*+4@ec#{=MV!^)IfajbCO9A}(knkF#q_6XQ*<<*p}PwXzHA>u1a6ACDonU{#b5_<{5XMZu<4WZ`;Jx z(h^cMuHk1Qctiw^6rY+1fBi zjs1Hlnh1@8&1al6ryC_h6ie7YmO7Q+FW=(K^?j8~nlsPc`W;@B*DJoBqXyosl*Y zFcRh9fO4To4qx@{Vj~o@pzE-x6qL;JDQ*15+$b?^6(mA?b%s3RY?ZaKD^J(1oZFCU zVV|VlY7v813zuG!cY#a?q%zE^OUz*TMI_=}98xXJ){4U$Rhi1P%b55@!f#~3;~}1K zZTSJNnCZGk+np2CjFPYVn!x1K2YvP1-@o`gtt3Llo`Y&zSe$}E>mzQi%Puq+-1`+` zu*I?go>@}+X|9DVNnZplAxjIr%o4LJF=rOQg<;5bWX|{YToyG?zAw*T9EgzL@Ij5r zm!XSzzWM}MG63Hc=hbY*Ev_zG<=g0l9!ZZse-8ARB;LcW!47PP&i#@tOgcPZFl#y*50p3YCwH0Ds4Z7BB zx8dlVJZQSC=T2@Tud0>dt^ngek9-^>NGz)Z(gzO@y7-&qFlnv3vz)kRipD);O(`s1 z$GJ$Kb2v(>=((N*B0V=-jA=Pjn<_FK0?MV~v~r~%oaDOBAc-3__DWPo>Fm$QYmZ9& zmAP#;xV}>LyQ3=*7RD>j?Li@mU<;AEzvFhcxqs>{d@K6QGUKhL2wEA-ywObYnu47V z$l4E{Cf#6r+2^{UqFRzu&J;Z%5vi^SuCCljt)67%*`ZCdF3IM5^c}|BCp5+1Fh`No znd?R=c)3oO@{KnAA&H+uLx)F7dh&v0Jxr}S_>Wemv8fH|k^zp)R0dq|MV8t~-iG6i z!h4G)NyJDF%sq*1WXYi@n@+Mfw|oxNM0AThf!%$t&i=rqb2#G34hGmfX(v}OS@$X z)XjF@mltL$n#?Ivd!(Sl;NU@meG-{hDcqLjR^dKv*50uEme*q`L-$%G4n&8@v$p0u zh+{2Yd*xx|QXiPwEXKz?n00FU%cQ{$B|`gNWkT5;EV;=8S%PsRD-K29s-b~4g_ZiL zwH2%uR!g`ibutlq%|FH!2i|9^+5H}*f&arDkU}&f8jNByz>`%_d zkn2lRfX?v3U5i{t-vHg*+0j|1gAOl4Bj+jGFITxP0Z?UWnn{qbs{}hg;*8^C!uPJS z-BQ7TN*IsVntY^iFJ<7kXjZPdr}nNxX*cV>+h98nq2=8IwI20EKRc|$ky_6+B9DA9 zs1jR`i(z%>tqng0qblOe<=`!hm?kqbc`&8}#;8Y);o_vr<9ViZJ@ImS{^y#%-Xyz& zESkM_tOF=qWiM>`b0$@Z2(}C+N4_$*FO|)=`R<5sdSv&}PmJ);M5K!ER#=*}>X3oP zm!=vCL=!I+v+C{F+yHGatburM*+YndTKK#`bcLT;S` zg1rV_7!Buy-|+BkG=3T+sL#vXS@E8uUT4MN(GQ_Kc3I6w%N7jrtr}fQ-e7Uq@s@9Y z5f6c7Joqo|--}=W6a`Ai9CIkq!0j1!4~~MlEw7gmlT_)V_7Zko@5ux7s_vX!T#qfJeTA_Qr`6g_IQ8TE)r#Y~hYw zy4+Epwro!TCRKjQS~f?vZTIf{Np*u^;p9#!&{Un_zYjt{TeP*IYa6Q<NcO!uj)j5QMu3gu)-?NqKv+nkp6bI@=5(`%IfE%YPXe^o$D3r^aaI~XCo z9NN42D^fC@L?7eT$HgU+^#@ds=Zl2-U~BD!xWdlWLS?0f^FEeszC*o!wD$b#UuC-i zHsueh{GNHu4#Rz`4C&=Vw^T1)yLudHz4+K99&)v!c0F0&{eB39Gi8`GrruAy z;%Ixek`#4>!y9Gid12-{?o=d4TINE%Z9cVGNSgI9YF5{st&DhiTE_)5_<7tbhNdn$ zu$NFiAEj5sqCCCb)~u-6V;iIx-!N8rc0W;UgU`K4%=g-QINfE$YLmSyZVcNCW2O2X zsUpn$6W0D%)T6{8n5Wwnq_$GlNFfVR_qk<9xY?yuTEg4@S%nhjL4%^u6%jGPjLZ^YIMLs*r;LnkX-Y-tn2a zSPf{{`^yUHHyOf1WkHg*1ydK1iF!^Z^9uM$dcA;Wg0>`VSI^ZL$u=_SQ5s9c54i7=H=JT-0BQ2IlWpN}{F8m)dh!-wVU|X;ab~K3G&TNQITJ-qEkyf2`)}I- z8`j8HS&TU*#ZKn!9s8vk2uv(q;7PA0cqc}e}5b~E!C+u%q znU@S$Ax1BLOpff|^MHIeHKwaG4~L+7LX46&7JzTC>#2)es0!+v*QZ=6J*Jq*MI)B? z>}^F^o32QR46c(Xf6I}hI89OqU)Rl&+h(g!)xUm)6w*GY>-ooGU>LoCzkYrJhI*#D z7;Ps+6=7j~FS^c&1a3MjoIcZN)f7O|jt_g$#yOV-zq{hGqIm2NpZ&ZT54ob~>jBv( LTMD)O|Nj2~#I)V2 diff --git a/resources/icons/toolbar_arrow.svg b/resources/icons/toolbar_arrow.svg index 998646a24c..b496451907 100644 --- a/resources/icons/toolbar_arrow.svg +++ b/resources/icons/toolbar_arrow.svg @@ -1,76 +1,22 @@ - -image/svg+xml - \ No newline at end of file diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 227e5ae4e5..1dd9f1d236 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4438,13 +4438,7 @@ bool GLCanvas3D::_init_main_toolbar() } // init arrow BackgroundTexture::Metadata arrow_data; - arrow_data.filename = "toolbar_arrow.png"; -// arrow_data.filename = "toolbar_arrow.svg"; - //arrow_data.left = 16; - //arrow_data.top = 16; - //arrow_data.right = 16; - //arrow_data.bottom = 16; - + arrow_data.filename = "toolbar_arrow.svg"; arrow_data.left = 0; arrow_data.top = 0; arrow_data.right = 0; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 2430318472..515b6ed3a3 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -193,10 +193,9 @@ bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture) std::string path = resources_dir() + "/icons/"; bool res = false; - if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false); -// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100); - + if (!arrow_texture.filename.empty()) { + res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); + } if (res) m_arrow_texture.metadata = arrow_texture; @@ -1176,19 +1175,22 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte float right = left + scaled_icons_size; unsigned int tex_id = m_arrow_texture.texture.get_id(); + // width and height of icon arrow is pointing to float tex_width = (float)m_icons_texture.get_width(); float tex_height = (float)m_icons_texture.get_height(); + // arrow width and height + float arr_tex_width = (float)m_arrow_texture.texture.get_width(); + float arr_tex_height = (float)m_arrow_texture.texture.get_height(); + if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) { + float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f; + float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f; - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) { - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow - float internal_right = right - border + scaled_icons_size / 2; + float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow + float internal_right = right - border + scaled_icons_size * 1.5f; float internal_top = top - border; // bottom is not moving and should be calculated from arrow texture sides ratio float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio; + float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ; float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 5617b5ed89..6d9a039772 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -132,8 +132,7 @@ bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_textur bool res = false; if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false); -// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100); + res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); if (res) m_arrow_texture.metadata = arrow_texture; @@ -1019,7 +1018,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); + GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); break; } zoomed_top_y -= zoomed_stride_y; From 673a2bdac8e9874f3fbec15d2568b5cf69018c45 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 18:25:37 +0200 Subject: [PATCH 235/240] Fix: ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:109:23: warning: comparison of integer expressions of different signedness: 'std::basic_string::size_type' {aka 'long unsigned int'} and 'int' [-Wsign-compare] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:132:17: warning: format string is not a string literal (potentially insecure) [-Wformat-security] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:171:17: warning: format string is not a string literal (potentially insecure) [-Wformat-security] Severity Code Description Project File Line Suppression State Warning C26451 Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '-' to avoid overflow (io.2). libslic3r_gui C:\GIT\slic3r\src\slic3r\GUI\Gizmos\GLGizmoSimplify.cpp 143 --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 22 +++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 3530b4240e..5ce0064db0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -129,7 +129,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); m_imgui->disabled_begin(m_configuration.use_count); - ImGui::Text(tr_detail_level.c_str()); + ImGui::Text("%s", tr_detail_level.c_str()); std::vector reduce_captions = { static_cast(_u8L("Extra high")), static_cast(_u8L("High")), @@ -139,17 +139,17 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi }; ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); - static int reduction = 3; - if(ImGui::SliderInt("##ReductionLevel", &reduction, 1, 5, reduce_captions[reduction-1].c_str())) { + static int reduction = 2; + if(ImGui::SliderInt("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) { m_is_valid_result = false; - if (reduction < 1) reduction = 1; - if (reduction > 5) reduction = 5; + if (reduction < 0) reduction = 0; + if (reduction > 4) reduction = 4; switch (reduction) { - case 1: m_configuration.max_error = 1e-3f; break; - case 2: m_configuration.max_error = 1e-2f; break; - case 3: m_configuration.max_error = 0.1f; break; - case 4: m_configuration.max_error = 0.5f; break; - case 5: m_configuration.max_error = 1.f; break; + case 0: m_configuration.max_error = 1e-3f; break; + case 1: m_configuration.max_error = 1e-2f; break; + case 2: m_configuration.max_error = 0.1f; break; + case 3: m_configuration.max_error = 0.5f; break; + case 4: m_configuration.max_error = 1.f; break; } } m_imgui->disabled_end(); // !use_count @@ -168,7 +168,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } m_imgui->disabled_begin(!m_configuration.use_count); - ImGui::Text(tr_decimate_ratio.c_str()); + ImGui::Text("%s", tr_decimate_ratio.c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%": diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 9af6686bd1..04d4b99abc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -87,7 +87,7 @@ private: int window_padding = 0; // trunc model name when longer - int max_char_in_name = 30; + size_t max_char_in_name = 30; }; std::optional m_gui_cfg; From 08b91d33e9eb49c3412095e5877be715be0e6845 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 26 Aug 2021 08:06:37 +0200 Subject: [PATCH 236/240] Fixed update of objects list in sidebar after deleting a part from a sinking object --- src/slic3r/GUI/GUI_ObjectList.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 23ce819dd4..39676e2d7a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2730,8 +2730,9 @@ void ObjectList::delete_from_model_and_list(const std::vector& it return; m_prevent_list_events = true; - for (std::vector::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) - { + + std::set modified_objects_ids; + for (std::vector::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) { if (!(item->type&(itObject | itVolume | itInstance))) continue; if (item->type&itObject) { @@ -2741,8 +2742,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it else { if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) continue; - if (item->type&itVolume) - { + if (item->type&itVolume) { m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx)); ModelObject* obj = object(item->obj_idx); if (obj->volumes.size() == 1) { @@ -2760,7 +2760,14 @@ void ObjectList::delete_from_model_and_list(const std::vector& it else m_objects_model->Delete(m_objects_model->GetItemByInstanceId(item->obj_idx, item->sub_obj_idx)); } + + modified_objects_ids.insert(static_cast(item->obj_idx)); } + + for (size_t id : modified_objects_ids) { + update_info_items(id); + } + m_prevent_list_events = true; part_selection_changed(); } From 67bc29299ae023578473eea43f0ea846be165940 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 26 Aug 2021 08:34:43 +0200 Subject: [PATCH 237/240] Fixed sinking object popping up on bed when adding a modifier --- src/libslic3r/Model.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e32285106e..6654d3a130 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -959,8 +959,12 @@ void ModelObject::ensure_on_bed(bool allow_negative_z) double z_offset = 0.0; if (allow_negative_z) { - if (parts_count() == 1) - z_offset = -get_min_z(); + if (parts_count() == 1) { + const double min_z = get_min_z(); + const double max_z = get_max_z(); + if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0) + z_offset = -min_z; + } else { const double max_z = get_max_z(); if (max_z < SINKING_MIN_Z_THRESHOLD) From 1c25078bc3aa9b91ef2bcc1307f1c21a6346cc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 26 Aug 2021 08:41:11 +0200 Subject: [PATCH 238/240] Added a missing include (GCC 11.1). --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 04d4b99abc..1b25c4ac91 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -7,6 +7,7 @@ #include "admesh/stl.h" // indexed_triangle_set #include #include +#include namespace Slic3r { From af413e37bbac2046d3c79ef92bd4a823a14fbd65 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 26 Aug 2021 10:38:42 +0200 Subject: [PATCH 239/240] Deps: Change empty CMAKE_BUILD_TYPE to Release on single config generators --- deps/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 3ce6b88a93..bc98e0b836 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -57,6 +57,11 @@ set(PATCH_CMD ${GIT_EXECUTABLE} apply --verbose --ignore-space-change --whitespa get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) + message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") +endif () + function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) From 95e4ab94609d854f733623349b427ee3781ce364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 26 Aug 2021 12:04:57 +0200 Subject: [PATCH 240/240] Fixed the issue that an object disappeared in a multi-material painting gizmo when the object was almost completely sunk under the bed. Object disappearance was caused by calling glPolygonOffset for rendered triangles in the multi-material painting gizmo to resolve z-fighting between painted triangles and contours around selected areas using seed/bucket fill. --- resources/shaders/mm_contour.fs | 5 +++++ src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/resources/shaders/mm_contour.fs b/resources/shaders/mm_contour.fs index 14c18dcf16..8ccf5b832c 100644 --- a/resources/shaders/mm_contour.fs +++ b/resources/shaders/mm_contour.fs @@ -1,6 +1,11 @@ #version 110 +const float EPSILON = 0.0001; + void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + // Values inside depth buffer for fragments of the contour of a selected area are offset + // by small epsilon to solve z-fighting between painted triangles and contour lines. + gl_FragDepth = gl_FragCoord.z - EPSILON; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 3d8be9eff0..a69319d3b6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -630,7 +630,11 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) auto *contour_shader = wxGetApp().get_shader("mm_contour"); contour_shader->start_using(); + + glsafe(::glDepthFunc(GL_LEQUAL)); m_gizmo_scene.render_contour(); + glsafe(::glDepthFunc(GL_LESS)); + contour_shader->stop_using(); } @@ -725,10 +729,6 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const assert(this->vertices_VBO_id != 0); assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0); - ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); }); - glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); - glsafe(::glPolygonOffset(5.0, 5.0)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id)); glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));