From d6deb1b9f47fbbc3dac11ad6f966a020da642165 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Sep 2020 17:33:08 +0200 Subject: [PATCH 01/16] Fixed compilation with wxWidgets 3.0 --- src/slic3r/GUI/GCodeViewer.cpp | 2 ++ src/slic3r/GUI/GUI_Utils.hpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0e3378e9a1a..8ab17717c7c 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index a68a0a88ab9..ad6bca72085 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -116,7 +116,7 @@ public: this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent& evt) { m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT; - m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize(); + m_new_font_point_size = get_default_font_for_dpi(this, evt.dpi).GetPointSize(); if (!m_can_rescale) return; From 00973011947230ef760a77db7c1244a54968f1bb Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 25 Sep 2020 09:07:52 +0200 Subject: [PATCH 02/16] Experiment with spherical cursor (painting gizmos) --- src/libslic3r/TriangleSelector.cpp | 8 +++++--- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 9f04374fdc0..abbc3bc566f 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -61,7 +61,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, // add neighboring facets to list to be proccessed later for (int n=0; n<3; ++n) { int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n]; - if (neighbor_idx >=0 && faces_camera(neighbor_idx)) + if (neighbor_idx >=0 && true/*faces_camera(neighbor_idx)*/) facets_to_check.push_back(neighbor_idx); } } @@ -206,8 +206,10 @@ void TriangleSelector::split_triangle(int facet_idx) // Calculate distance of a point from a line. bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const { - Vec3f diff = m_cursor.center - point; - return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; + Vec3f diff = m_cursor.center - point; + // return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; + + return diff.squaredNorm() < m_cursor.radius_sqr; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 939d3c48a0b..4b3b9e52bae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -119,6 +119,8 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const void GLGizmoPainterBase::render_cursor_circle() const { + return; + const Camera& camera = wxGetApp().plater()->get_camera(); float zoom = (float)camera.get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; From 1b6c75e98f5b1efa75bce15a2a0163ea0493490b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Sep 2020 17:01:51 +0200 Subject: [PATCH 03/16] Sphere selection added as an option for painting gizmos --- src/libslic3r/TriangleSelector.cpp | 13 ++-- src/libslic3r/TriangleSelector.hpp | 7 +++ src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 64 +++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 7 +++ src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 20 +++++- 6 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index abbc3bc566f..1462b1a7647 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -35,14 +35,15 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& source, const Vec3f& dir, - float radius, EnforcerBlockerType new_state) + float radius, CursorType cursor_type, + EnforcerBlockerType new_state) { assert(facet_start < m_orig_size_indices); assert(is_approx(dir.norm(), 1.f)); // Save current cursor center, squared radius and camera direction, // so we don't have to pass it around. - m_cursor = {hit, source, dir, radius*radius}; + m_cursor = {hit, source, dir, radius*radius, cursor_type}; // In case user changed cursor size since last time, update triangle edge limit. if (m_old_cursor_radius != radius) { @@ -61,7 +62,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, // add neighboring facets to list to be proccessed later for (int n=0; n<3; ++n) { int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n]; - if (neighbor_idx >=0 && true/*faces_camera(neighbor_idx)*/) + if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx))) facets_to_check.push_back(neighbor_idx); } } @@ -207,9 +208,11 @@ void TriangleSelector::split_triangle(int facet_idx) bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const { Vec3f diff = m_cursor.center - point; - // return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; - return diff.squaredNorm() < m_cursor.radius_sqr; + if (m_cursor.type == CIRCLE) + return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; + else // SPHERE + return diff.squaredNorm() < m_cursor.radius_sqr; } diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index be1b20ed40c..899539c8e73 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -17,6 +17,11 @@ enum class EnforcerBlockerType : int8_t; // to recursively subdivide the triangles and make the selection finer. class TriangleSelector { public: + enum CursorType { + CIRCLE, + SPHERE + }; + void set_edge_limit(float edge_limit); // Create new object on a TriangleMesh. The referenced mesh must @@ -29,6 +34,7 @@ public: const Vec3f& source, // camera position (mesh coords) const Vec3f& dir, // direction of the ray (mesh coords) float radius, // radius of the cursor + CursorType type, // current type of cursor EnforcerBlockerType new_state); // enforcer or blocker? // Get facets currently in the given state. @@ -127,6 +133,7 @@ protected: Vec3f source; Vec3f dir; float radius_sqr; + CursorType type; }; Cursor m_cursor; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 6b3456b60e4..58346a41475 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -66,7 +66,7 @@ void GLGizmoFdmSupports::on_render() const render_triangles(selection); m_c->object_clipper()->render_cut(); - render_cursor_circle(); + render_cursor(); glsafe(::glDisable(GL_BLEND)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 4b3b9e52bae..e7d3e17c2dd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -21,6 +21,14 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic : GLGizmoBase(parent, icon_filename, sprite_id) { m_clipping_plane.reset(new ClippingPlane()); + // Make sphere and save it into a vertex buffer. + const TriangleMesh sphere_mesh = make_sphere(1., (2*M_PI)/24.); + for (size_t i=0; i(), + sphere_mesh.stl.facet_start[i].normal.cast()); + for (const stl_triangle_vertex_indices& indices : sphere_mesh.its.indices) + m_vbo_sphere.push_triangle(indices(0), indices(1), indices(2)); + m_vbo_sphere.finalize_geometry(true); } @@ -117,10 +125,18 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const } +void GLGizmoPainterBase::render_cursor() const +{ + if (m_cursor_type == TriangleSelector::SPHERE) + render_cursor_sphere(); + else + render_cursor_circle(); +} + + + void GLGizmoPainterBase::render_cursor_circle() const { - return; - const Camera& camera = wxGetApp().plater()->get_camera(); float zoom = (float)camera.get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; @@ -164,6 +180,46 @@ void GLGizmoPainterBase::render_cursor_circle() const } +void GLGizmoPainterBase::render_cursor_sphere() const +{ + int mesh_id = m_last_mesh_idx_and_hit.first; + if (mesh_id == -1) + return; + + const Vec3f hit_pos = m_last_mesh_idx_and_hit.second; + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelVolume* mv = mo->volumes[mesh_id]; + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + const Transform3d instance_matrix = mi->get_transformation().get_matrix() * mv->get_matrix(); + const Transform3d instance_scaling_matrix_inverse = Geometry::Transformation(instance_matrix).get_matrix(true, true, false, true).inverse(); + const bool is_left_handed = Geometry::Transformation(instance_matrix).is_left_handed(); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixd(instance_matrix.data())); + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glTranslatef(hit_pos(0), hit_pos(1), hit_pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius)); + + if (is_left_handed) + glFrontFace(GL_CW); + + float render_color[4] = { 0.f, 0.f, 0.f, 0.15f }; + if (m_button_down == Button::Left) + render_color[2] = 1.f; + else // right + render_color[0] = 1.f; + glsafe(::glColor4fv(render_color)); + + m_vbo_sphere.render(); + + if (is_left_handed) + glFrontFace(GL_CCW); + + glsafe(::glPopMatrix()); +} + bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const { @@ -354,8 +410,9 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous assert(mesh_id < int(m_triangle_selectors.size())); m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, - dir, limit, new_state); + dir, limit, m_cursor_type, new_state); m_last_mouse_position = mouse_position; + m_last_mesh_idx_and_hit = {mesh_id, closest_hit}; } return true; @@ -392,6 +449,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_button_down = Button::None; m_last_mouse_position = Vec2d::Zero(); + m_last_mesh_idx_and_hit = {-1, Vec3f::Zero()}; return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index b3e2b65f1fb..47bd2660868 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -67,10 +67,13 @@ public: protected: void render_triangles(const Selection& selection) const; + void render_cursor() const; void render_cursor_circle() const; + void render_cursor_sphere() const; virtual void update_model_object() const = 0; virtual void update_from_model_object() = 0; void activate_internal_undo_redo_stack(bool activate); + void set_cursor_type(TriangleSelector::CursorType); float m_cursor_radius = 2.f; static constexpr float CursorRadiusMin = 0.4f; // cannot be zero @@ -80,16 +83,20 @@ protected: // For each model-part volume, store status and division of the triangles. std::vector> m_triangle_selectors; + TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE; + private: bool is_mesh_point_clipped(const Vec3d& point) const; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; + GLIndexedVertexArray m_vbo_sphere; bool m_internal_stack_active = false; bool m_schedule_update = false; Vec2d m_last_mouse_position = Vec2d::Zero(); + std::pair m_last_mesh_idx_and_hit = {-1, Vec3f::Zero()}; enum class Button { None, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index d0edfba131f..1b1b1f10f5a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -25,6 +25,7 @@ bool GLGizmoSeam::on_init() m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["cursor_type"] = _L("Cursor size") + ": "; m_desc["enforce_caption"] = _L("Left mouse button") + ": "; m_desc["enforce"] = _L("Enforce seam"); m_desc["block_caption"] = _L("Right mouse button") + " "; @@ -55,7 +56,7 @@ void GLGizmoSeam::on_render() const render_triangles(selection); m_c->object_clipper()->render_cut(); - render_cursor_circle(); + render_cursor(); glsafe(::glDisable(GL_BLEND)); } @@ -134,6 +135,23 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) ImGui::EndTooltip(); } + + m_imgui->text(m_desc.at("cursor_type")); + ImGui::SameLine(/*clipping_slider_left*/); + //ImGui::PushItemWidth(window_width - clipping_slider_left); + int selection = int(m_cursor_type); + m_imgui->combo(" ", {"Circle", "Sphere"}, selection); + m_cursor_type = TriangleSelector::CursorType(selection); + /*if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + }*/ + + + ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) m_imgui->text(m_desc.at("clipping_of_view")); From 9de2729ff27dbd36a8ca3b92314e3495ae54e305 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Sep 2020 22:10:03 +0200 Subject: [PATCH 04/16] Slight refactoring --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 29 +++++++------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index e7d3e17c2dd..ef4b3187796 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -314,9 +314,15 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) + trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + } + // Now "click" into all the prepared points and spill paint around them. for (const Vec2d& mp : mouse_positions) { - std::vector>> hit_positions_and_facet_ids; bool clipped_mesh_was_hit = false; Vec3f normal = Vec3f::Zero(); @@ -327,9 +333,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous size_t closest_facet = 0; int closest_hit_mesh_id = -1; - // Transformations of individual meshes - std::vector trafo_matrices; - int mesh_id = -1; // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (const ModelVolume* mv : mo->volumes) { @@ -338,9 +341,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous ++mesh_id; - trafo_matrices.push_back(instance_trafo * mv->get_matrix()); - hit_positions_and_facet_ids.push_back(std::vector>()); - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( mp, trafo_matrices[mesh_id], @@ -366,18 +366,19 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } } } + mesh_id = closest_hit_mesh_id; bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); // The mouse button click detection is enabled when there is a valid hit // or when the user clicks the clipping plane. Missing the object entirely // shall not capture the mouse. - if (closest_hit_mesh_id != -1 || clipped_mesh_was_hit) { + if (mesh_id != -1 || clipped_mesh_was_hit) { if (m_button_down == Button::None) m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); } - if (closest_hit_mesh_id == -1) { + if (mesh_id == -1) { // In case we have no valid hit, we can return. The event will // be stopped in following two cases: // 1. clicking the clipping plane @@ -386,16 +387,6 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous || dragging_while_painting; } - // Find respective mesh id. - mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++mesh_id; - if (mesh_id == closest_hit_mesh_id) - break; - } - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; // Calculate how far can a point be from the line (in mesh coords). From 9c3877fde1d39d0eb40d7edf2975f6aacd0b25ec Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Sep 2020 22:28:49 +0200 Subject: [PATCH 05/16] Moved the raycasting query in painting gizmos to a separate function This way it can be called when rendering the spherical cursor and when processing the mouse clicks/drags --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 93 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 6 ++ 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index ef4b3187796..1c5edbb0362 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -323,50 +323,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Now "click" into all the prepared points and spill paint around them. for (const Vec2d& mp : mouse_positions) { - bool clipped_mesh_was_hit = false; - Vec3f normal = Vec3f::Zero(); + bool clipped_mesh_was_hit = false; Vec3f hit = Vec3f::Zero(); size_t facet = 0; - Vec3f closest_hit = Vec3f::Zero(); - double closest_hit_squared_distance = std::numeric_limits::max(); - size_t closest_facet = 0; - int closest_hit_mesh_id = -1; - int mesh_id = -1; - // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - ++mesh_id; - - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( - mp, - trafo_matrices[mesh_id], - camera, - hit, - normal, - m_clipping_plane.get(), - &facet)) - { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast())) { - clipped_mesh_was_hit = true; - continue; - } - - // Is this hit the closest to the camera so far? - double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); - if (hit_squared_distance < closest_hit_squared_distance) { - closest_hit_squared_distance = hit_squared_distance; - closest_facet = facet; - closest_hit_mesh_id = mesh_id; - closest_hit = hit; - } - } - } - mesh_id = closest_hit_mesh_id; + get_mesh_hit(mp, camera, trafo_matrices, mesh_id, hit, facet, clipped_mesh_was_hit); bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); @@ -397,13 +360,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (closest_hit - camera_pos).normalized(); + Vec3f dir = (hit - camera_pos).normalized(); assert(mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[mesh_id]->select_patch(closest_hit, closest_facet, camera_pos, + m_triangle_selectors[mesh_id]->select_patch(hit, facet, camera_pos, dir, limit, m_cursor_type, new_state); m_last_mouse_position = mouse_position; - m_last_mesh_idx_and_hit = {mesh_id, closest_hit}; + m_last_mesh_idx_and_hit = {mesh_id, hit}; } return true; @@ -448,6 +411,52 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } +void GLGizmoPainterBase::get_mesh_hit(const Vec2d& mouse_position, + const Camera& camera, + const std::vector& trafo_matrices, + int& mesh_id, Vec3f& hit, size_t& facet, + bool& clipped_mesh_was_hit) const +{ + Vec3f normal = Vec3f::Zero(); + size_t current_facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + size_t closest_facet = 0; + int closest_hit_mesh_id = -1; + + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { + + if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( + mouse_position, + trafo_matrices[mesh_id], + camera, + hit, + normal, + m_clipping_plane.get(), + ¤t_facet)) + { + // In case this hit is clipped, skip it. + if (is_mesh_point_clipped(hit.cast())) { + clipped_mesh_was_hit = true; + continue; + } + + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_facet = current_facet; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + } + } + } + + mesh_id = closest_hit_mesh_id; + facet = closest_facet; + hit = closest_hit; +} bool GLGizmoPainterBase::on_is_activable() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 47bd2660868..177bad53dd4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -21,6 +21,7 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; +class Camera; enum class PainterGizmoType { FDM_SUPPORTS, @@ -88,6 +89,11 @@ protected: private: bool is_mesh_point_clipped(const Vec3d& point) const; + void get_mesh_hit(const Vec2d& mouse_position, + const Camera& camera, + const std::vector& trafo_matrices, + int& mesh_id, Vec3f& hit, size_t& facet, + bool& clipped_mesh_was_hit) const; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; From f54bb097ff770fb6d32e38fbb4874af919d08154 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 1 Oct 2020 00:03:20 +0200 Subject: [PATCH 06/16] Cache raycast results so they don't have to be repeated on the same mouse pos Fixed incorrect handling of clipping plane with multiple volumes - only the first volume was correctly clipped by the painter. --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 118 +++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 25 ++-- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 1c5edbb0362..c421e63de9d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -182,33 +182,41 @@ void GLGizmoPainterBase::render_cursor_circle() const void GLGizmoPainterBase::render_cursor_sphere() const { - int mesh_id = m_last_mesh_idx_and_hit.first; - if (mesh_id == -1) + Vec2d mouse_position(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); + + const ModelObject* mo = m_c->selection_info()->model_object(); + const Selection& selection = m_parent.get_selection(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + const Camera& camera = wxGetApp().plater()->get_camera(); + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) + trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + } + update_raycast_cache(mouse_position, camera, trafo_matrices); + if (m_rr.mesh_id == -1) return; - const Vec3f hit_pos = m_last_mesh_idx_and_hit.second; - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelVolume* mv = mo->volumes[mesh_id]; - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - const Transform3d instance_matrix = mi->get_transformation().get_matrix() * mv->get_matrix(); - const Transform3d instance_scaling_matrix_inverse = Geometry::Transformation(instance_matrix).get_matrix(true, true, false, true).inverse(); - const bool is_left_handed = Geometry::Transformation(instance_matrix).is_left_handed(); + const Transform3d& complete_matrix = trafo_matrices[m_rr.mesh_id]; + const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(complete_matrix).get_matrix(true, true, false, true).inverse(); + const bool is_left_handed = Geometry::Transformation(complete_matrix).is_left_handed(); glsafe(::glPushMatrix()); - glsafe(::glMultMatrixd(instance_matrix.data())); + glsafe(::glMultMatrixd(complete_matrix.data())); // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glTranslatef(hit_pos(0), hit_pos(1), hit_pos(2))); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + glsafe(::glTranslatef(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))); + glsafe(::glMultMatrixd(complete_scaling_matrix_inverse.data())); glsafe(::glScaled(m_cursor_radius, m_cursor_radius, m_cursor_radius)); if (is_left_handed) glFrontFace(GL_CW); - float render_color[4] = { 0.f, 0.f, 0.f, 0.15f }; + float render_color[4] = { 0.f, 0.f, 0.f, 0.25f }; if (m_button_down == Button::Left) render_color[2] = 1.f; - else // right + else if (m_button_down == Button::Right) render_color[0] = 1.f; glsafe(::glColor4fv(render_color)); @@ -221,16 +229,12 @@ void GLGizmoPainterBase::render_cursor_sphere() const } -bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point) const +bool GLGizmoPainterBase::is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const { if (m_c->object_clipper()->get_position() == 0.) return false; auto sel_info = m_c->selection_info(); - int active_inst = m_c->selection_info()->get_active_instance(); - const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; - const Transform3d& trafo = mi->get_transformation().get_matrix(); - Vec3d transformed_point = trafo * point; transformed_point(2) += sel_info->get_sla_shift(); return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); @@ -299,20 +303,20 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // add several positions from between into the list, so there // are no gaps in the painted region. { - if (m_last_mouse_position == Vec2d::Zero()) - m_last_mouse_position = mouse_position; + if (m_last_mouse_click == Vec2d::Zero()) + m_last_mouse_click = mouse_position; // resolution describes minimal distance limit using circle radius // as a unit (e.g., 2 would mean the patches will be touching). double resolution = 0.7; double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); - int patches_in_between = int(((mouse_position - m_last_mouse_position).norm() - diameter_px) / diameter_px); + int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px); if (patches_in_between > 0) { - Vec2d diff = (mouse_position - m_last_mouse_position)/(patches_in_between+1); + Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1); for (int i=1; i<=patches_in_between; ++i) - mouse_positions.emplace_back(m_last_mouse_position + i*diff); + mouse_positions.emplace_back(m_last_mouse_click + i*diff); } } - m_last_mouse_position = Vec2d::Zero(); // only actual hits should be saved + m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved // Precalculate transformations of individual meshes. std::vector trafo_matrices; @@ -323,34 +327,28 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Now "click" into all the prepared points and spill paint around them. for (const Vec2d& mp : mouse_positions) { - - bool clipped_mesh_was_hit = false; - Vec3f hit = Vec3f::Zero(); - size_t facet = 0; - int mesh_id = -1; - - get_mesh_hit(mp, camera, trafo_matrices, mesh_id, hit, facet, clipped_mesh_was_hit); + update_raycast_cache(mp, camera, trafo_matrices); bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); // The mouse button click detection is enabled when there is a valid hit // or when the user clicks the clipping plane. Missing the object entirely // shall not capture the mouse. - if (mesh_id != -1 || clipped_mesh_was_hit) { + if (m_rr.mesh_id != -1 || m_rr.clipped_mesh_was_hit) { if (m_button_down == Button::None) m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); } - if (mesh_id == -1) { + if (m_rr.mesh_id == -1) { // In case we have no valid hit, we can return. The event will // be stopped in following two cases: // 1. clicking the clipping plane // 2. dragging while painting (to prevent scene rotations and moving the object) - return clipped_mesh_was_hit + return m_rr.clipped_mesh_was_hit || dragging_while_painting; } - const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; + const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id]; // Calculate how far can a point be from the line (in mesh coords). // FIXME: The scaling of the mesh can be non-uniform. @@ -360,13 +358,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (hit - camera_pos).normalized(); + Vec3f dir = (m_rr.hit - camera_pos).normalized(); - assert(mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[mesh_id]->select_patch(hit, facet, camera_pos, + assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, dir, limit, m_cursor_type, new_state); - m_last_mouse_position = mouse_position; - m_last_mesh_idx_and_hit = {mesh_id, hit}; + m_last_mouse_click = mouse_position; } return true; @@ -402,8 +399,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous update_model_object(); m_button_down = Button::None; - m_last_mouse_position = Vec2d::Zero(); - m_last_mesh_idx_and_hit = {-1, Vec3f::Zero()}; + m_last_mouse_click = Vec2d::Zero(); return true; } @@ -411,21 +407,27 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } -void GLGizmoPainterBase::get_mesh_hit(const Vec2d& mouse_position, - const Camera& camera, - const std::vector& trafo_matrices, - int& mesh_id, Vec3f& hit, size_t& facet, - bool& clipped_mesh_was_hit) const + +void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position, + const Camera& camera, + const std::vector& trafo_matrices) const { + if (m_rr.mouse_position == mouse_position) { + // Same query as last time - the answer is already in the cache. + return; + } + + bool clipped_mesh_was_hit{false}; Vec3f normal = Vec3f::Zero(); - size_t current_facet = 0; + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; Vec3f closest_hit = Vec3f::Zero(); double closest_hit_squared_distance = std::numeric_limits::max(); size_t closest_facet = 0; int closest_hit_mesh_id = -1; // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { + for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( mouse_position, @@ -434,10 +436,10 @@ void GLGizmoPainterBase::get_mesh_hit(const Vec2d& mouse_position, hit, normal, m_clipping_plane.get(), - ¤t_facet)) + &facet)) { // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast())) { + if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) { clipped_mesh_was_hit = true; continue; } @@ -446,16 +448,14 @@ void GLGizmoPainterBase::get_mesh_hit(const Vec2d& mouse_position, double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); if (hit_squared_distance < closest_hit_squared_distance) { closest_hit_squared_distance = hit_squared_distance; - closest_facet = current_facet; + closest_facet = facet; closest_hit_mesh_id = mesh_id; closest_hit = hit; } } } - mesh_id = closest_hit_mesh_id; - facet = closest_facet; - hit = closest_hit; + m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_facet, clipped_mesh_was_hit}; } bool GLGizmoPainterBase::on_is_activable() const @@ -564,13 +564,13 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) m_iva_blockers.finalize_geometry(true); if (m_iva_enforcers.has_VBOs()) { - ::glColor4f(0.f, 0.f, 1.f, 0.3f); + ::glColor4f(0.f, 0.f, 1.f, 0.4f); m_iva_enforcers.render(); } if (m_iva_blockers.has_VBOs()) { - ::glColor4f(1.f, 0.f, 0.f, 0.3f); + ::glColor4f(1.f, 0.f, 0.f, 0.4f); m_iva_blockers.render(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 177bad53dd4..02b4dd1c79c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -88,12 +88,10 @@ protected: private: - bool is_mesh_point_clipped(const Vec3d& point) const; - void get_mesh_hit(const Vec2d& mouse_position, - const Camera& camera, - const std::vector& trafo_matrices, - int& mesh_id, Vec3f& hit, size_t& facet, - bool& clipped_mesh_was_hit) const; + bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const; + void update_raycast_cache(const Vec2d& mouse_position, + const Camera& camera, + const std::vector& trafo_matrices) const; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; @@ -101,8 +99,7 @@ private: bool m_internal_stack_active = false; bool m_schedule_update = false; - Vec2d m_last_mouse_position = Vec2d::Zero(); - std::pair m_last_mesh_idx_and_hit = {-1, Vec3f::Zero()}; + Vec2d m_last_mouse_click = Vec2d::Zero(); enum class Button { None, @@ -113,6 +110,18 @@ private: Button m_button_down = Button::None; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) + // Following cache holds result of a raycast query. The queries are asked + // during rendering the sphere cursor and painting, this saves repeated + // raycasts when the mouse position is the same as before. + struct RaycastResult { + Vec2d mouse_position; + int mesh_id; + Vec3f hit; + size_t facet; + bool clipped_mesh_was_hit; + }; + mutable RaycastResult m_rr; + protected: void on_set_state() override; void on_start_dragging() override {} From a186a4cafc09654a9e962223478ec423bc1b35ff Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 1 Oct 2020 00:41:19 +0200 Subject: [PATCH 07/16] Imgui dialog layout adjustments after the new combo was added --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 37 ++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 38 +++++++++++++------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 58346a41475..04288c13f66 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -42,6 +42,7 @@ bool GLGizmoFdmSupports::on_init() m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["cursor_type"] = _L("Cursor type") + ": "; m_desc["enforce_caption"] = _L("Left mouse button") + ": "; m_desc["enforce"] = _L("Enforce supports"); m_desc["block_caption"] = _L("Right mouse button") + " "; @@ -78,16 +79,26 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l if (! m_c->selection_info()->model_object()) return; - const float approx_height = m_imgui->scaled(18.0f); + const float approx_height = m_imgui->scaled(14.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); if (! m_setting_angle) { m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + std::vector cursor_types; + cursor_types.push_back(_L("Circle").ToUTF8().data()); + cursor_types.push_back(_L("Sphere").ToUTF8().data()); + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + + m_imgui->scaled(1.5f); const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float cursor_type_combo_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); + const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x, + m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x) + + m_imgui->scaled(2.5f); const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); @@ -103,6 +114,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, button_width); + window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width); auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); @@ -139,8 +151,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SameLine(cursor_slider_left); + ImGui::PushItemWidth(window_width - cursor_slider_left); ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -150,6 +162,23 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::EndTooltip(); } + + m_imgui->text(m_desc.at("cursor_type")); + ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f)); + ImGui::PushItemWidth(cursor_type_combo_width); + int selection = int(m_cursor_type); + m_imgui->combo("", cursor_types, selection); + m_cursor_type = TriangleSelector::CursorType(selection); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n" + "Circle ignores facets facing away from the camera.").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) m_imgui->text(m_desc.at("clipping_of_view")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 1b1b1f10f5a..0cbfaeedcff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -25,7 +25,7 @@ bool GLGizmoSeam::on_init() m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Cursor size") + ": "; - m_desc["cursor_type"] = _L("Cursor size") + ": "; + m_desc["cursor_type"] = _L("Cursor type") + ": "; m_desc["enforce_caption"] = _L("Left mouse button") + ": "; m_desc["enforce"] = _L("Enforce seam"); m_desc["block_caption"] = _L("Right mouse button") + " "; @@ -68,16 +68,26 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) if (! m_c->selection_info()->model_object()) return; - const float approx_height = m_imgui->scaled(18.0f); + const float approx_height = m_imgui->scaled(14.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + std::vector cursor_types; + cursor_types.push_back(_L("Circle").ToUTF8().data()); + cursor_types.push_back(_L("Sphere").ToUTF8().data()); + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + + m_imgui->scaled(1.5f); + const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float cursor_type_combo_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); + const float cursor_type_combo_width = std::max(m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[0])).x, + m_imgui->calc_text_size(wxString::FromUTF8(cursor_types[1])).x) + + m_imgui->scaled(2.5f); const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); @@ -90,9 +100,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) caption_max += m_imgui->scaled(1.f); total_text_max += m_imgui->scaled(1.f); - float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + float window_width = minimal_slider_width + std::max(cursor_size_slider_left, clipping_slider_left); window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, button_width); + window_width = std::max(window_width, cursor_type_combo_left + cursor_type_combo_width); auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); @@ -124,8 +135,8 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; m_imgui->text(m_desc.at("cursor_size")); - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SameLine(cursor_size_slider_left); + ImGui::PushItemWidth(window_width - cursor_size_slider_left); ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -137,18 +148,19 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) m_imgui->text(m_desc.at("cursor_type")); - ImGui::SameLine(/*clipping_slider_left*/); - //ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SameLine(window_width - cursor_type_combo_width - m_imgui->scaled(0.5f)); + ImGui::PushItemWidth(cursor_type_combo_width); int selection = int(m_cursor_type); - m_imgui->combo(" ", {"Circle", "Sphere"}, selection); + m_imgui->combo("", cursor_types, selection); m_cursor_type = TriangleSelector::CursorType(selection); - /*if (ImGui::IsItemHovered()) { + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(max_tooltip_width); - ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::TextUnformatted(_L("Sphere paints all facets inside, regardless of their orientation.\n\n" + "Circle ignores facets facing away from the camera.").ToUTF8().data()); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); - }*/ + } From 28070e012cddea362d8b232c26893b9c7e602fa7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Oct 2020 08:34:16 +0200 Subject: [PATCH 08/16] Fixed typo --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 02b4dd1c79c..8a9e871248e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -21,7 +21,7 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; -class Camera; +struct Camera; enum class PainterGizmoType { FDM_SUPPORTS, From 39d44ecc6c325963cae259ebc1c489b640ffdcf9 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 1 Oct 2020 09:25:11 +0200 Subject: [PATCH 09/16] Filament selection in configuration wizard: compatible printers in html window, bug fixes. --- src/slic3r/GUI/ConfigWizard.cpp | 362 ++++++++++++++++++------ src/slic3r/GUI/ConfigWizard_private.hpp | 50 ++-- 2 files changed, 293 insertions(+), 119 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index c98b736b7c6..9c4338d8634 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -566,20 +566,20 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin , list_type(new StringList(this)) , list_vendor(new StringList(this)) , list_profile(new PresetList(this)) - , compatible_printers(new wxStaticText(this, wxID_ANY, _(L("")))) { append_spacer(VERTICAL_SPACING); const int em = parent->em_unit(); const int list_h = 30*em; - list_printer->SetWindowStyle(wxLB_EXTENDED); list_printer->SetMinSize(wxSize(23*em, list_h)); list_type->SetMinSize(wxSize(8*em, list_h)); list_vendor->SetMinSize(wxSize(13*em, list_h)); list_profile->SetMinSize(wxSize(23*em, list_h)); + + grid = new wxFlexGridSizer(4, em/2, em); grid->AddGrowableCol(3, 1); grid->AddGrowableRow(1, 1); @@ -601,17 +601,19 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin btn_sizer->Add(sel_none); + grid->Add(new wxBoxSizer(wxHORIZONTAL)); grid->Add(new wxBoxSizer(wxHORIZONTAL)); grid->Add(new wxBoxSizer(wxHORIZONTAL)); grid->Add(btn_sizer, 0, wxALIGN_RIGHT); - - auto* notes_sizer = new wxBoxSizer(wxHORIZONTAL); - notes_sizer->Add(compatible_printers); - grid->Add(notes_sizer); - append(grid, 1, wxEXPAND); + append_spacer(VERTICAL_SPACING); + + html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, + wxSize(60 * em, 20 * em), wxHW_SCROLLBAR_AUTO); + append(html_window, 0, wxEXPAND); + list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) { update_lists(evt.GetInt(), list_type->GetSelection(), list_vendor->GetSelection()); }); @@ -627,28 +629,25 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); }); sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); }); - + /* Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {on_paint();}); list_profile->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) { on_mouse_move_on_profiles(evt); }); list_profile->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& evt) { on_mouse_enter_profiles(evt); }); list_profile->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& evt) { on_mouse_leave_profiles(evt); }); - + */ reload_presets(); + set_compatible_printers_html_window(std::vector(), false); } void PageMaterials::on_paint() { - if (first_paint) { - first_paint = false; - prepare_compatible_printers_label(); - } } void PageMaterials::on_mouse_move_on_profiles(wxMouseEvent& evt) { const wxClientDC dc(list_profile); const wxPoint pos = evt.GetLogicalPosition(dc); int item = list_profile->HitTest(pos); - BOOST_LOG_TRIVIAL(error) << "hit test: " << item; + //BOOST_LOG_TRIVIAL(debug) << "hit test: " << item; on_material_hovered(item); } void PageMaterials::on_mouse_enter_profiles(wxMouseEvent& evt) @@ -666,10 +665,11 @@ void PageMaterials::reload_presets() for (const Preset* printer : materials->printers) { list_printer->append(printer->name, &printer->name); } - + sort_list_data(list_printer, true, false); if (list_printer->GetCount() > 0) { list_printer->SetSelection(0); - sel_printer_prev = wxNOT_FOUND; + sel_printer_count_prev = wxNOT_FOUND; + sel_printer_item_prev = wxNOT_FOUND; sel_type_prev = wxNOT_FOUND; sel_vendor_prev = wxNOT_FOUND; update_lists(0, 0, 0); @@ -678,34 +678,105 @@ void PageMaterials::reload_presets() presets_loaded = true; } -void PageMaterials::prepare_compatible_printers_label() +void PageMaterials::set_compatible_printers_html_window(const std::vector& printer_names, bool all_printers) { - assert(grid->GetColWidths().size() == 4); - compatible_printers_width = grid->GetColWidths()[3]; - empty_printers_label = "Compatible printers:"; - for (const Preset* printer : materials->printers) { - empty_printers_label += "\n"; + //Slic3r::GUI::wxGetApp().dark_mode() + const auto bgr_clr = +#if defined(__APPLE__) + wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); +#else + wxSystemSettings::GetColour(wxSYS_COLOUR_MENU); +#endif + const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); + const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); + wxString first_line = L"Profiles marked with * are not compatible with all installed printers."; + wxString text; + if (all_printers) { + text = wxString::Format( + "" + "" + "" + "" + "" + "%s

All installed printers are compatible with selected profile." + "
" + "
" + "" + "" + , bgr_clr_str + , text_clr_str + , first_line + ); + } else { + wxString second_line = L"Compatible printers:"; + text = wxString::Format( + "" + "" + "" + "" + "" + "%s

%s" + "" + "" + , bgr_clr_str + , text_clr_str + , first_line + , second_line); + for (int i = 0; i < printer_names.size(); ++i) + { + text += wxString::Format("", boost::nowide::widen(printer_names[i])); + if (i % 3 == 2) { + text += wxString::Format( + "" + ""); + } + } + text += wxString::Format( + "" + "
%s
" + "
" + "
" + "" + "" + ); } - clear_compatible_printers_label(); + + wxFont font = get_default_font_for_dpi(get_dpi_for_window(this)); + const int fs = font.GetPointSize(); + int size[] = { fs,fs,fs,fs,fs,fs,fs }; + html_window->SetFonts(font.GetFaceName(), font.GetFaceName(), size); + html_window->SetPage(text); } void PageMaterials::clear_compatible_printers_label() { - compatible_printers->SetLabel(boost::nowide::widen(empty_printers_label)); - compatible_printers->Wrap(compatible_printers_width); - Layout(); + set_compatible_printers_html_window(std::vector(), false); } void PageMaterials::on_material_hovered(int sel_material) { - if ( sel_material == last_hovered_item) + +} + +void PageMaterials::on_material_highlighted(int sel_material) +{ + if (sel_material == last_hovered_item) return; if (sel_material == -1) { clear_compatible_printers_label(); return; } last_hovered_item = sel_material; - std::string compatible_printers_label = "compatible printers:\n"; + std::string compatible_printers_label = "Compatible printers:\n"; + std::vector tabs; + tabs.push_back(std::string()); + tabs.push_back(std::string()); + tabs.push_back(std::string()); //selected material string std::string material_name = list_profile->get_data(sel_material); // get material preset @@ -716,70 +787,16 @@ void PageMaterials::on_material_hovered(int sel_material) return; } //find matching printers - bool first = true; + std::vector names; for (const Preset* printer : materials->printers) { - bool compatible = false; for (const Preset* material : matching_materials) { if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) { - if (first) - first = false; - else - compatible_printers_label += "\n";//", "; - compatible_printers_label += printer->name; - compatible = true; + names.push_back(printer->name); break; } } } - this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label)); - this->compatible_printers->Wrap(compatible_printers_width); -} - -void PageMaterials::on_material_highlighted(int sel_material) -{ - wxWindowUpdateLocker freeze_guard(this); - (void)freeze_guard; - - //std::string compatible_printers_label = "compatible printers:\n"; - //std::string empty_suplement = std::string(); - //unselect all printers - list_printer->SetSelection(wxNOT_FOUND); - //selected material string - std::string material_name = list_profile->get_data(sel_material); - // get material preset - const std::vector matching_materials = materials->get_presets_by_alias(material_name); - if (matching_materials.empty()) - return; - //find matching printers - //bool first = true; - for (const Preset* printer : materials->printers) { - bool compatible = false; - for (const Preset* material : matching_materials) { - if (is_compatible_with_printer(PresetWithVendorProfile(*material, material->vendor), PresetWithVendorProfile(*printer, printer->vendor))) { - //select printer - int index = list_printer->find(printer->name); - list_printer->SetSelection(index); - /*if (first) - first = false; - else - compatible_printers_label += "\n";//", "; - compatible_printers_label += printer->name; - compatible = true; - break;*/ - } - } - //if(!compatible) - // empty_suplement += std::string(printer->name.length() + 2, ' '); - } - // fill rest of label with blanks so it maintains legth - //compatible_printers_label += empty_suplement; - - update_lists(0,0,0); - list_profile->SetSelection(list_profile->find(material_name)); - - //this->compatible_printers->SetLabel(boost::nowide::widen(compatible_printers_label)); - //this->compatible_printers->Wrap(compatible_printers_width); - //Refresh(); + set_compatible_printers_html_window(names, names.size() == materials->printers.size()); } void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) @@ -790,7 +807,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) wxArrayInt sel_printers; int sel_printers_count = list_printer->GetSelections(sel_printers); - if (sel_printers_count != sel_printer_prev) { + if (sel_printers_count != sel_printer_count_prev || (sel_printers_count == 1 && sel_printer_item_prev != sel_printer && sel_printer != -1)) { // Refresh type list list_type->Clear(); list_type->append(_(L("(All)")), &EMPTY); @@ -827,6 +844,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) //clear selection except "ALL" list_printer->SetSelection(wxNOT_FOUND); list_printer->SetSelection(0); + sel_printers_count = list_printer->GetSelections(sel_printers); materials->filter_presets(nullptr, EMPTY, EMPTY, [this](const Preset* p) { const std::string& type = this->materials->get_type(p); @@ -835,10 +853,11 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) } }); } - + sort_list_data(list_type, true, true); } - sel_printer_prev = sel_printers_count; + sel_printer_count_prev = sel_printers_count; + sel_printer_item_prev = sel_printer; sel_type = 0; sel_type_prev = wxNOT_FOUND; list_type->SetSelection(sel_type); @@ -872,6 +891,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) } }); } + sort_list_data(list_vendor, true, false); } sel_type_prev = sel_type; @@ -905,7 +925,6 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) //size_t printer_counter = materials->get_printer_counter(p); int cur_i = list_profile->find(p->alias); if (cur_i == wxNOT_FOUND) - //cur_i = list_profile->append(p->alias + " " + std::to_string(printer_counter)/*+ (omnipresent ? "" : " ONLY SOME PRINTERS")*/, &p->alias); cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias); else was_checked = list_profile->IsChecked(cur_i); @@ -925,12 +944,103 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor) wizard_p()->appconfig_new.set(section, p->name, "1"); }); } + sort_list_data(list_profile); } sel_vendor_prev = sel_vendor; } } +void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering) +{ +// get data from list +// sort data +// first should be +// then prusa profiles +// then the rest +// in alphabetical order + + std::vector> prusa_profiles; + std::vector> other_profiles; + for (int i = 0 ; i < list->size(); ++i) { + const std::string& data = list->get_data(i); + if (data == EMPTY) // do not sort item + continue; + if (!material_type_ordering && data.find("Prusa") != std::string::npos) + prusa_profiles.push_back(data); + else + other_profiles.push_back(data); + } + if(material_type_ordering) { + + const ConfigOptionDef* def = print_config_def.get("filament_type"); + std::vectorenum_values = def->enum_values; + int end_of_sorted = 0; + for (size_t vals = 0; vals < enum_values.size(); vals++) { + for (size_t profs = end_of_sorted; profs < other_profiles.size(); profs++) + { + // find instead compare because PET vs PETG + if (other_profiles[profs].get().find(enum_values[vals]) != std::string::npos) { + //swap + if(profs != end_of_sorted) { + std::reference_wrapper aux = other_profiles[end_of_sorted]; + other_profiles[end_of_sorted] = other_profiles[profs]; + other_profiles[profs] = aux; + } + end_of_sorted++; + break; + } + } + } + } else { + std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() < b.get(); + }); + std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() < b.get(); + }); + } + + list->Clear(); + if (add_All_item) + list->append(_(L("(All)")), &EMPTY); + for (const auto& item : prusa_profiles) + list->append(item, &const_cast(item.get())); + for (const auto& item : other_profiles) + list->append(item, &const_cast(item.get())); +} + +void PageMaterials::sort_list_data(PresetList* list) +{ + // sort data + // then prusa profiles + // then the rest + // in alphabetical order + std::vector> prusa_profiles; + std::vector> other_profiles; + for (int i = 0; i < list->size(); ++i) { + const std::string& data = list->get_data(i); + if (data == EMPTY) // do not sort item + continue; + if (data.find("Prusa") != std::string::npos) + prusa_profiles.push_back(data); + else + other_profiles.push_back(data); + } + std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() < b.get(); + }); + std::sort(other_profiles.begin(), other_profiles.end(), [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() < b.get(); + }); + list->Clear(); + for (const auto& item : prusa_profiles) + list->append(item, &const_cast(item.get())); + for (const auto& item : other_profiles) + list->append(item, &const_cast(item.get())); + +} + void PageMaterials::select_material(int i) { const bool checked = list_profile->IsChecked(i); @@ -959,7 +1069,8 @@ void PageMaterials::clear() list_type->Clear(); list_vendor->Clear(); list_profile->Clear(); - sel_printer_prev = wxNOT_FOUND; + sel_printer_count_prev = wxNOT_FOUND; + sel_printer_item_prev = wxNOT_FOUND; sel_type_prev = wxNOT_FOUND; sel_vendor_prev = wxNOT_FOUND; presets_loaded = false; @@ -1546,7 +1657,7 @@ const std::string Materials::UNKNOWN = "(Unknown)"; void Materials::push(const Preset *preset) { - presets.emplace_back(preset, 0); + presets.emplace_back(preset); types.insert(technology & T_FFF ? Materials::get_filament_type(preset) : Materials::get_material_type(preset)); @@ -1562,6 +1673,7 @@ void Materials::clear() presets.clear(); types.clear(); printers.clear(); + compatibility_counter.clear(); } const std::string& Materials::appconfig_section() const @@ -1855,13 +1967,45 @@ void ConfigWizard::priv::update_materials(Technology technology) if (!filament.alias.empty()) aliases_fff[filament.alias].insert(filament.name); } - filaments.add_printer_counter(&filament); filaments.add_printer(&printer); } } } } + // count compatible printers + for (const auto& preset : filaments.presets) { + + const auto filter = [preset](const std::pair element) { + return preset->alias == element.first; + }; + if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) { + continue; + } + std::vector idx_with_same_alias; + for (size_t i = 0; i < filaments.presets.size(); ++i) { + if (preset->alias == filaments.presets[i]->alias) + idx_with_same_alias.push_back(i); + } + size_t counter = 0; + for (const auto& printer : filaments.printers) { + if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF) + continue; + bool compatible = false; + // Test otrher materials with same alias + for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { + const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]); + const Preset& prntr = *printer; + if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { + compatible = true; + break; + } + } + if (compatible) + counter++; + } + filaments.compatibility_counter.emplace_back(preset->alias, counter); + } } if (any_sla_selected && (technology & T_SLA)) { @@ -1887,12 +2031,44 @@ void ConfigWizard::priv::update_materials(Technology technology) if (!material.alias.empty()) aliases_sla[material.alias].insert(material.name); } - sla_materials.add_printer_counter(&material); sla_materials.add_printer(&printer); } } } } + // count compatible printers + for (const auto& preset : sla_materials.presets) { + + const auto filter = [preset](const std::pair element) { + return preset->alias == element.first; + }; + if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) { + continue; + } + std::vector idx_with_same_alias; + for (size_t i = 0; i < sla_materials.presets.size(); ++i) { + if(preset->alias == sla_materials.presets[i]->alias) + idx_with_same_alias.push_back(i); + } + size_t counter = 0; + for (const auto& printer : sla_materials.printers) { + if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA) + continue; + bool compatible = false; + // Test otrher materials with same alias + for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { + const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]); + const Preset& prntr = *printer; + if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { + compatible = true; + break; + } + } + if (compatible) + counter++; + } + sla_materials.compatibility_counter.emplace_back(preset->alias, counter); + } } } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 260eeb22cb7..850f8fd46eb 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" @@ -86,9 +87,9 @@ struct Materials { Technology technology; // use vector for the presets to purpose of save of presets sorting in the bundle - // bool is true if material is present in all printers (omnipresent) - // size_t is counter of printers compatible with material - std::vector> presets; + std::vector presets; + // String is alias of material, size_t number of compatible counters + std::vector> compatibility_counter; std::set types; std::set printers; @@ -100,7 +101,7 @@ struct Materials bool containts(const Preset *preset) const { //return std::find(presets.begin(), presets.end(), preset) != presets.end(); return std::find_if(presets.begin(), presets.end(), - [preset](const std::pair& element) { return element.first == preset; }) != presets.end(); + [preset](const Preset* element) { return element == preset; }) != presets.end(); } @@ -111,42 +112,35 @@ struct Materials const std::vector get_presets_by_alias(const std::string name) { std::vector ret_vec; for (auto it = presets.begin(); it != presets.end(); ++it) { - if ((*it).first->alias == name) - ret_vec.push_back((*it).first); + if ((*it)->alias == name) + ret_vec.push_back((*it)); } return ret_vec; } - void add_printer_counter(const Preset* preset) { - for (auto it = presets.begin(); it != presets.end(); ++it) { - if ((*it).first->alias == preset->alias) - (*it).second += 1; - } - } + size_t get_printer_counter(const Preset* preset) { - size_t highest = 0; - for (auto it : presets) { - if (it.first->alias == preset->alias && it.second > highest) - highest = it.second; - } - return highest; + for (auto it : compatibility_counter) { + if (it.first == preset->alias) + return it.second; + } + return 0; } const std::string& appconfig_section() const; const std::string& get_type(const Preset *preset) const; const std::string& get_vendor(const Preset *preset) const; - template void filter_presets(const Preset* printer, const std::string& type, const std::string& vendor, F cb) { for (auto preset : presets) { - const Preset& prst = *(preset.first); + const Preset& prst = *(preset); const Preset& prntr = *printer; if ((printer == nullptr || is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) && - (type.empty() || get_type(preset.first) == type) && - (vendor.empty() || get_vendor(preset.first) == vendor)) { + (type.empty() || get_type(preset) == type) && + (vendor.empty() || get_vendor(preset) == vendor)) { - cb(preset.first); + cb(preset); } } } @@ -325,11 +319,12 @@ struct PageMaterials: ConfigWizardPage Materials *materials; StringList *list_printer, *list_type, *list_vendor; PresetList *list_profile; - int sel_printer_prev, sel_type_prev, sel_vendor_prev; + int sel_printer_count_prev, sel_printer_item_prev, sel_type_prev, sel_vendor_prev; bool presets_loaded; wxFlexGridSizer *grid; - wxStaticText *compatible_printers; + wxHtmlWindow* html_window; + int compatible_printers_width = { 100 }; std::string empty_printers_label; bool first_paint = { false }; @@ -345,9 +340,12 @@ struct PageMaterials: ConfigWizardPage void select_material(int i); void select_all(bool select); void clear(); - void prepare_compatible_printers_label(); + void set_compatible_printers_html_window(const std::vector& printer_names, bool all_printers = false); void clear_compatible_printers_label(); + void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering); + void sort_list_data(PresetList* list); + void on_paint(); void on_mouse_move_on_profiles(wxMouseEvent& evt); void on_mouse_enter_profiles(wxMouseEvent& evt); From 03feeed8c984e8c381e8aebcf854bab34e069a9a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 21 Sep 2020 09:14:47 +0200 Subject: [PATCH 10/16] notification orange color for hypertext --- src/slic3r/GUI/NotificationManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 47962f4b2a1..d5ab9ee1ace 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -362,7 +362,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, ImGui::PopStyleColor(); //hover color - ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); + ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button); if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) orange_color.y += 0.2f; From 630859ef3919a9521864983baf1fc4dd21d9de6f Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 21 Sep 2020 13:47:18 +0200 Subject: [PATCH 11/16] notifications: changed some plater warnings into errors, fixed not showing plater warnings in preview. --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/NotificationManager.cpp | 9 +++++---- src/slic3r/GUI/NotificationManager.hpp | 5 +++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a3b4be9a7ac..c138b937c9c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -628,8 +628,8 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool bool error = false; switch (warning) { case ObjectOutside: text = L("An object outside the print area was detected."); break; - case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break; - case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break; + case ToolpathOutside: text = L("A toolpath outside the print area was detected."); error = true; break; + case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); error = true; break; case SomethingNotShown: text = L("Some objects are not visible."); break; case ObjectClashed: text = L( "An object outside the print area was detected.\n" @@ -644,7 +644,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); } else { if (error) - wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); + wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(text); else wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text); } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index d5ab9ee1ace..b9e7c7a6fce 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -56,8 +56,7 @@ NotificationManager::PopNotification::~PopNotification() } NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y) { - if (!m_initialized) - { + if (!m_initialized) { init(); } if (m_finished) @@ -682,11 +681,13 @@ void NotificationManager::push_plater_error_notification(const std::string& text void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas) { push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, canvas, 0); + // dissaper if in preview + set_in_preview(m_in_preview); } -void NotificationManager::close_plater_error_notification() +void NotificationManager::close_plater_error_notification(const std::string& text) { for (PopNotification* notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::PlaterError) { + if (notification->get_type() == NotificationType::PlaterError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) { notification->close(); } } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index a11d08394cc..c2cbc242c82 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -220,7 +220,8 @@ public: void compare_warning_oids(const std::vector& living_oids); void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas); void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas); - void close_plater_error_notification(); + // Closes error or warning of same text + void close_plater_error_notification(const std::string& text); void close_plater_warning_notification(const std::string& text); // creates special notification slicing complete // if large = true prints printing time and export button @@ -250,7 +251,7 @@ private: bool m_hovered { false }; //timestamps used for slining finished - notification could be gone so it needs to be stored here std::unordered_set m_used_timestamps; - bool m_in_preview; + bool m_in_preview { false }; //prepared (basic) notifications const std::vector basic_notifications = { From 3020a12d0e896e77284fccf8b80ea436d7c2f403 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Oct 2020 09:33:05 +0200 Subject: [PATCH 12/16] Improved performance of progress dialog shown while generating toolpaths for render --- src/slic3r/GUI/GCodeViewer.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8ab17717c7c..a6914f76810 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -863,6 +863,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (m_moves_count == 0) return; + unsigned int progress_count = 0; + static const unsigned int progress_threshold = 1000; wxProgressDialog progress_dialog(_L("Generating toolpaths"), "...", 100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL); @@ -1245,10 +1247,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (i == 0) continue; - progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))), - _L("Generating vertex buffer") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" + - wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")"); - progress_dialog.Fit(); + ++progress_count; + if (progress_count % progress_threshold == 0) { + progress_dialog.Update(int(100.0f * float(i) / (2.0f * float(m_moves_count))), + _L("Generating vertex buffer") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%"); + progress_dialog.Fit(); + progress_count = 0; + } const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; @@ -1316,10 +1321,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) if (i == 0) continue; - progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))), - _L("Generating index buffers") + " (" + wxNumberFormatter::ToString((long)i, wxNumberFormatter::Style_None) + "/" + - wxNumberFormatter::ToString((long)m_moves_count, wxNumberFormatter::Style_None) + ")"); - progress_dialog.Fit(); + ++progress_count; + if (progress_count % progress_threshold == 0) { + progress_dialog.Update(int(100.0f * float(m_moves_count + i) / (2.0f * float(m_moves_count))), + _L("Generating index buffers") + ": " + wxNumberFormatter::ToString(100.0 * double(i) / double(m_moves_count), 0, wxNumberFormatter::Style_None) + "%"); + progress_dialog.Fit(); + progress_count = 0; + } const GCodeProcessor::MoveVertex& prev = gcode_result.moves[i - 1]; const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i]; From 8346bd367950d41cca469796cffc8b8383baad1b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 1 Oct 2020 09:45:36 +0200 Subject: [PATCH 13/16] fix in ConfigWizard.cpp --- 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 9c4338d8634..cfc81f5a7d5 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -746,7 +746,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vectorSetFonts(font.GetFaceName(), font.GetFaceName(), size); From 140cf15222e7e5434c83e7742b2d809677bf6f76 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Oct 2020 15:11:56 +0200 Subject: [PATCH 14/16] Maybe one day we will be able to run PrusaGCodeViewer, but for now the Apple notarization process refuses Apps with multiple binaries and Vojtech does not know any workaround. Just run PrusaSlicer and give it a --gcodeviewer parameter. --- src/slic3r/Utils/Process.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index 375c10a6a5c..d2a326221af 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -56,11 +56,17 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); #if defined(__APPLE__) { - bin_path = bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); + // Maybe one day we will be able to run PrusaGCodeViewer, but for now the Apple notarization + // process refuses Apps with multiple binaries and Vojtech does not know any workaround. + // ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); + // Just run PrusaSlicer and give it a --gcodeviewer parameter. + bin_path = bin_path.parent_path() / "PrusaSlicer"; // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { std::vector args; + if (instance_type == NewSlicerInstanceType::GCodeViewer) + args.emplace_back("--gcodeviewer"); if (path_to_open) args.emplace_back(into_u8(*path_to_open)); boost::process::spawn(bin_path, args); From b1ce406f33266dc70c758feb277f94cccec45f6b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Oct 2020 19:58:23 +0200 Subject: [PATCH 15/16] Win32 specific: Workaround for tooltips over Tree Controls displayed over excessively long tree control items, stealing the window focus. In case the Tab was reparented from the MainFrame to the floating dialog, the tooltip created by the Tree Control before reparenting is not reparented, but it still points to the MainFrame. If the tooltip pops up, the MainFrame is incorrectly focussed, stealing focus from the floating dialog. The workaround is to delete the tooltip control. Vojtech tried to reparent the tooltip control, but it did not work, and if the Tab was later reparented back to MainFrame, the tooltip was displayed at an incorrect position, therefore it is safer to just discard the tooltip control altogether. --- src/slic3r/GUI/Tab.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index de268fc7b3b..aad698e5d9c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -394,6 +394,31 @@ void Tab::OnActivate() Fit(); m_size_move *= -1; #endif // __WXOSX__ + +#ifdef __WXMSW__ + // Workaround for tooltips over Tree Controls displayed over excessively long + // tree control items, stealing the window focus. + // + // In case the Tab was reparented from the MainFrame to the floating dialog, + // the tooltip created by the Tree Control before reparenting is not reparented, + // but it still points to the MainFrame. If the tooltip pops up, the MainFrame + // is incorrectly focussed, stealing focus from the floating dialog. + // + // The workaround is to delete the tooltip control. + // Vojtech tried to reparent the tooltip control, but it did not work, + // and if the Tab was later reparented back to MainFrame, the tooltip was displayed + // at an incorrect position, therefore it is safer to just discard the tooltip control + // altogether. + HWND hwnd_tt = TreeView_GetToolTips(m_treectrl->GetHandle()); + if (hwnd_tt) { + HWND hwnd_toplevel = find_toplevel_parent(m_treectrl)->GetHandle(); + HWND hwnd_parent = ::GetParent(hwnd_tt); + if (hwnd_parent != hwnd_toplevel) { + ::DestroyWindow(hwnd_tt); + TreeView_SetToolTips(m_treectrl->GetHandle(), nullptr); + } + } +#endif } void Tab::update_labels_colour() From c53e14d6d4bc4d292c2183a13322c392480b657e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 1 Oct 2020 20:15:40 +0200 Subject: [PATCH 16/16] Fixing a missing include on Windows. --- src/slic3r/GUI/Tab.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index aad698e5d9c..09d151fc7bf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -38,6 +38,10 @@ #include "PhysicalPrinterDialog.hpp" #include "UnsavedChangesDialog.hpp" +#ifdef WIN32 + #include +#endif // WIN32 + namespace Slic3r { namespace GUI {