From 8bf0f75e83846ba2588c75a3f4330175b9a87117 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 0e3378e9a1..8ab17717c7 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 a68a0a88ab..ad6bca7208 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 c696e6ec192cbf44e4073585999ff866d69f0e12 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 9f04374fdc..abbc3bc566 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 939d3c48a0..4b3b9e52ba 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 1ca8120398e00d37c439c32dad4994d836235750 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 abbc3bc566..1462b1a764 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 be1b20ed40..899539c8e7 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 6b3456b60e..58346a4147 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 4b3b9e52ba..e7d3e17c2d 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 b3e2b65f1f..47bd266086 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 d0edfba131..1b1b1f10f5 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 6744a40cd5dfadc65d43131d13635f24744600ed 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 e7d3e17c2d..ef4b318779 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 fac7e735acb219e5ef8fc891c7a202127e4f1e24 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 ef4b318779..1c5edbb036 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 47bd266086..177bad53dd 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 3ec5d9e2cfacb65fd49b707e29b28afe16abf2b2 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 1c5edbb036..c421e63de9 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 177bad53dd..02b4dd1c79 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 3f7d41df15e023271054720f31dd690afaeca671 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 58346a4147..04288c13f6 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 1b1b1f10f5..0cbfaeedcf 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 fad1f5e84f2de4970120639b369bd6fd0b99f888 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 02b4dd1c79..8a9e871248 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 43f122b5ee506e9e9093d12b9273ff7a02d9ee7d 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 c98b736b7c..9c4338d863 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 260eeb22cb..850f8fd46e 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 a47178557fcb6fd562ab6c6e96b008823e0945d8 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 47962f4b2a..d5ab9ee1ac 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 661534042b683bdb8d2624eaa7a5a140da271965 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 a3b4be9a7a..c138b937c9 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 d5ab9ee1ac..b9e7c7a6fc 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 a11d08394c..c2cbc242c8 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 8ded9dc0fdb80305955cd52a81b0e4c2eaaa9af8 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 8ab17717c7..a6914f7681 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 6a46708608a0114cfb64953f773402fe54d8de82 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 9c4338d863..cfc81f5a7d 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 b71e5c2763e80e652a6f3bb410ed9325b91479b2 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 375c10a6a5..d2a326221a 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 f35efb8fe58581fcab676a032423df492e8b3b42 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 de268fc7b3..aad698e5d9 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 50293c0f86861b32ae569ef1b0d892ae64a1cf55 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 aad698e5d9..09d151fc7b 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 {