diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 061c5bd50..fa865a8b1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -606,6 +606,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) assert(this->config.id() == rhs.config.id()); this->sla_support_points = rhs.sla_support_points; this->sla_points_status = rhs.sla_points_status; + this->sla_drain_holes = rhs.sla_drain_holes; this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment this->layer_height_profile = rhs.layer_height_profile; this->printable = rhs.printable; @@ -646,6 +647,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) assert(this->config.id() == rhs.config.id()); this->sla_support_points = std::move(rhs.sla_support_points); this->sla_points_status = std::move(rhs.sla_points_status); + this->sla_drain_holes = std::move(rhs.sla_drain_holes); this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment this->layer_height_profile = std::move(rhs.layer_height_profile); this->origin_translation = std::move(rhs.origin_translation); @@ -1099,6 +1101,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper) { upper->set_model(nullptr); upper->sla_support_points.clear(); + lower->sla_drain_holes.clear(); upper->sla_points_status = sla::PointsStatus::NoPoints; upper->clear_volumes(); upper->input_file = ""; @@ -1107,6 +1110,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_lower) { lower->set_model(nullptr); lower->sla_support_points.clear(); + lower->sla_drain_holes.clear(); lower->sla_points_status = sla::PointsStatus::NoPoints; lower->clear_volumes(); lower->input_file = ""; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 410c2d3ef..f0641a182 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -203,6 +203,9 @@ public: // the SLA gizmo and the backend). sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; + // Holes to be drilled into the object so resin can flow out + std::vector sla_drain_holes; + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment @@ -372,7 +375,7 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class(this)); Internal::StaticSerializationWrapper config_wrapper(config); - ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, printable, origin_translation, + ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 634c5611f..6e56b6de7 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -80,6 +80,39 @@ struct SupportPoint using SupportPoints = std::vector; +struct DrainHole +{ + Vec3f m_pos; + Vec3f m_normal; + float m_radius; + float m_height; + + DrainHole() + : m_pos(Vec3f::Zero()), m_normal(Vec3f::UnitZ()), m_radius(5.f), + m_height(10.f) + {} + + DrainHole(Vec3f position, Vec3f normal, float radius, float height) + : m_pos(position) + , m_normal(normal) + , m_radius(radius) + , m_height(height) + {} + + bool operator==(const DrainHole &sp) const + { + return (m_pos == sp.m_pos) && (m_normal == sp.m_normal) + && is_approx(m_radius, sp.m_radius) && is_approx(m_height, sp.m_height); + } + + bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } + + template void serialize(Archive &ar) + { + ar(m_pos, m_normal, m_radius, m_height); + } +}; + struct Contour3D; /// An index-triangle structure for libIGL functions. Also serves as an diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f0aaca9ad..806069663 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -52,6 +52,7 @@ bool GLGizmoHollow::on_init() m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; m_desc["reset_direction"] = _(L("Reset direction")); m_desc["hollow"] = _(L("Hollow")); + m_desc["show_supports"] = _(L("Show supports")); return true; } @@ -243,13 +244,13 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glsafe(::glMultMatrixd(instance_matrix.data())); float render_color[4]; - size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); + size_t cache_size = m_model_object->sla_drain_holes.size(); for (size_t i = 0; i < cache_size; ++i) { - const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; - const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; + const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; + const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped(support_point.pos.cast())) + if (is_mesh_point_clipped(drain_hole.m_pos.cast())) continue; // First decide about the color of the point. @@ -262,7 +263,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons } else { render_color[3] = 1.f; - if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active + if (size_t(m_hover_id) == i) { render_color[0] = 0.f; render_color[1] = 1.0f; render_color[2] = 1.0f; @@ -280,43 +281,34 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); + glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) glFrontFace(GL_CW); // Matrices set, we can render the point mark now. - // If in editing mode, we'll also render a cone pointing to the sphere. - if (m_editing_mode) { - // in case the normal is not yet cached, find and cache it - if (m_editing_cache[i].normal == Vec3f::Zero()) - m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * drain_hole.m_normal.cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + + const double cylinder_radius = double(drain_hole.m_radius) * RenderPointScale; //0.25; // mm + const double stick_out_length = 1.; + const double cone_height = m_new_hole_height + stick_out_length; + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -cone_height+stick_out_length)); + ::gluCylinder(m_quadric, cylinder_radius, cylinder_radius, cone_height, 24, 1); + glsafe(::glTranslated(0., 0., cone_height)); + ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); + glsafe(::glTranslated(0., 0., -cone_height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); + glsafe(::glPopMatrix()); - const double cone_radius = double(support_point.head_front_radius) * RenderPointScale; //0.25; // mm - const double cone_height = m_new_cone_height; - //const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.); - const double cone_rad_diff = m_new_cone_angle*cone_radius; - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -cone_height/2.)); - //::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1); - ::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1); - glsafe(::glTranslated(0., 0., cone_height)); - ::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1); - glsafe(::glTranslated(0., 0., -cone_height)); - glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, cone_radius+cone_rad_diff, 24, 1); - glsafe(::glPopMatrix()); - } - //::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); if (vol->is_left_handed()) glFrontFace(GL_CCW); - glsafe(::glPopMatrix()); } @@ -412,122 +404,121 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair pos_and_normal; - if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); - m_parent.set_as_dirty(); - m_wait_for_up_event = true; - } - else - return false; + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); + m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first, pos_and_normal.second, m_new_hole_radius, m_new_hole_height); + m_selected.push_back(false); + assert(m_selected.size == m_model_object->sla_drain_holes.size()); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; } else - select_point(NoPoints); + return false; + } + else + select_point(NoPoints); + return true; + } + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + // First collect positions of all the points in world coordinates. + Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + std::vector points; + for (unsigned int i=0; isla_drain_holes.size(); ++i) + points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].m_pos.cast()); + + // Now ask the rectangle which of the points are inside. + std::vector points_inside; + std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); + for (size_t idx : points_idxs) + points_inside.push_back(points[idx].cast()); + + // Only select/deselect points that are actually visible + for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + { + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); + } + return true; + } + + // left up with no selection rectangle + if (action == SLAGizmoEventType::LeftUp) { + if (m_wait_for_up_event) { + m_wait_for_up_event = false; + return true; + } + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_wait_for_up_event) + return true; // point has been placed and the button not released yet + // this prevents GLCanvas from starting scene rotation + + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); return true; } - // left up with selection rectangle - select points inside the rectangle: - if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { - // Is this a selection or deselection rectangle? - GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + return false; + } - // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - std::vector points; - for (unsigned int i=0; i()); + if (action == SLAGizmoEventType::Delete) { + // delete key pressed + delete_selected_points(); + return true; + } - // Now ask the rectangle which of the points are inside. - std::vector points_inside; - std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); - for (size_t idx : points_idxs) - points_inside.push_back(points[idx].cast()); - - // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) - { - if (rectangle_status == GLSelectionRectangle::Deselect) - unselect_point(points_idxs[idx]); - else - select_point(points_idxs[idx]); - } - return true; - } - - // left up with no selection rectangle - if (action == SLAGizmoEventType::LeftUp) { - if (m_wait_for_up_event) { - m_wait_for_up_event = false; - return true; - } - } - - // dragging the selection rectangle: - if (action == SLAGizmoEventType::Dragging) { - if (m_wait_for_up_event) - return true; // point has been placed and the button not released yet - // this prevents GLCanvas from starting scene rotation - - if (m_selection_rectangle.is_dragging()) { - m_selection_rectangle.dragging(mouse_position); - return true; - } - - return false; - } - - if (action == SLAGizmoEventType::Delete) { - // delete key pressed + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); delete_selected_points(); return true; } + return false; + } - if (action == SLAGizmoEventType::RightDown) { - if (m_hover_id != -1) { - select_point(NoPoints); - select_point(m_hover_id); - delete_selected_points(); - return true; - } - return false; - } - - if (action == SLAGizmoEventType::SelectAll) { - select_point(AllPoints); - return true; - } + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { @@ -552,39 +543,26 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos void GLGizmoHollow::delete_selected_points(bool force) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: delete_selected_points called out of editing mode!" << std::endl; - std::abort(); - } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); - for (unsigned int idx=0; idxsla_drain_holes.size(); ++idx) { + if (m_selected[idx]) { + m_selected.erase(m_selected.begin()+idx); + m_model_object->sla_drain_holes.erase(m_model_object->sla_drain_holes.begin() + (idx--)); } } select_point(NoPoints); - - //m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void GLGizmoHollow::on_update(const UpdateData& data) { - if (! m_editing_mode) - return; - else { - if (m_hover_id != -1) { - std::pair pos_and_normal; - if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) - return; - m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first; - m_editing_cache[m_hover_id].support_point.is_new_island = false; - m_editing_cache[m_hover_id].normal = pos_and_normal.second; - // Do not update immediately, wait until the mouse is released. - // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - } + if (m_hover_id != -1) { + std::pair pos_and_normal; + if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) + return; + m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first; + m_model_object->sla_drain_holes[m_hover_id].m_normal = pos_and_normal.second; } } @@ -661,17 +639,12 @@ ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_model_object) + if (! m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. RENDER_AGAIN: - //m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); - //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); - //ImGui::SetNextWindowSize(ImVec2(window_size)); - const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -679,7 +652,6 @@ RENDER_AGAIN: 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 settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).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 diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f); @@ -690,125 +662,72 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); - bool force_refresh = false; bool remove_selected = false; bool remove_all = false; - if (m_editing_mode) { + if (m_imgui->button(m_desc.at("hollow"))) + hollow_mesh(); - if (m_imgui->button(m_desc.at("hollow"))) - hollow_mesh(); + float diameter_upper_cap = 20.f; //static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; + if (m_new_hole_radius > diameter_upper_cap) + m_new_hole_radius = diameter_upper_cap; + m_imgui->text(m_desc.at("head_diameter")); + ImGui::SameLine(diameter_slider_left); + ImGui::PushItemWidth(window_width - diameter_slider_left); - float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; - if (m_new_point_head_diameter > diameter_upper_cap) - m_new_point_head_diameter = diameter_upper_cap; - m_imgui->text(m_desc.at("head_diameter")); - ImGui::SameLine(diameter_slider_left); - ImGui::PushItemWidth(window_width - diameter_slider_left); - - // Following is a nasty way to: - // - save the initial value of the slider before one starts messing with it - // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene - // - take correct undo/redo snapshot after the user is done with moving the slider - float initial_value = m_new_point_head_diameter; - ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); - if (ImGui::IsItemClicked()) { - if (m_old_point_head_diameter == 0.f) - m_old_point_head_diameter = initial_value; - } - if (ImGui::IsItemEdited()) { - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; - } - if (ImGui::IsItemDeactivatedAfterEdit()) { - // momentarily restore the old value to take snapshot - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f; - float backup = m_new_point_head_diameter; - m_new_point_head_diameter = m_old_point_head_diameter; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); - m_new_point_head_diameter = backup; - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; - m_old_point_head_diameter = 0.f; - } - - // !!!! Something as above should be done for the cone angle - m_imgui->text("Hole taper: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_new_cone_angle, -1.f, 1.f, "%.1f"); - m_imgui->text("Hole height: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_new_cone_height, 0.1f, 10.f, "%.1f"); - - m_imgui->disabled_begin(m_selection_empty); - remove_selected = m_imgui->button(m_desc.at("remove_selected")); - m_imgui->disabled_end(); - - m_imgui->disabled_begin(m_editing_cache.empty()); - remove_all = m_imgui->button(m_desc.at("remove_all")); - m_imgui->disabled_end(); - - m_imgui->text(" "); // vertical gap - - - m_imgui->text("Offset: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); - m_imgui->text("Adaptibility: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); + // Following is a nasty way to: + // - save the initial value of the slider before one starts messing with it + // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene + // - take correct undo/redo snapshot after the user is done with moving the slider + float initial_value = m_new_hole_radius; + ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f"); + if (ImGui::IsItemClicked()) { + if (m_old_hole_radius == 0.f) + m_old_hole_radius = initial_value; } - else { // not in editing mode: - m_imgui->text(m_desc.at("minimal_distance")); - ImGui::SameLine(settings_sliders_left); - ImGui::PushItemWidth(window_width - settings_sliders_left); - - std::vector opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"}); - float density = static_cast(opts[0])->value; - float minimal_point_distance = static_cast(opts[1])->value; - - ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider - - m_imgui->text(m_desc.at("points_density")); - ImGui::SameLine(settings_sliders_left); - - ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%"); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); - - if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo - m_minimal_point_distance_stash = minimal_point_distance; - m_density_stash = density; - } - if (slider_edited) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; - } - if (slider_released) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; - wxGetApp().obj_list()->update_and_show_object_settings_item(); - } - - - m_imgui->disabled_begin(m_normal_cache.empty()); - remove_all = m_imgui->button(m_desc.at("remove_all")); - m_imgui->disabled_end(); - + if (ImGui::IsItemEdited()) { + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + // momentarily restore the old value to take snapshot + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_old_hole_radius; + float backup = m_new_hole_radius; + m_new_hole_radius = m_old_hole_radius; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); + m_new_hole_radius = backup; + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_old_hole_radius = 0.f; } + // !!!! Something as above should be done for the undo/redo + m_imgui->text("Hole height: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_new_hole_height, 0.1f, 10.f, "%.1f"); + + m_imgui->disabled_begin(m_selection_empty); + remove_selected = m_imgui->button(m_desc.at("remove_selected")); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(m_model_object->sla_drain_holes.empty()); + remove_all = m_imgui->button(m_desc.at("remove_all")); + m_imgui->disabled_end(); + + m_imgui->text(" "); // vertical gap + + + m_imgui->text("Offset: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); + m_imgui->text("Adaptibility: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); // Following is rendered in both editing and non-editing mode: m_imgui->text(""); @@ -827,7 +746,6 @@ RENDER_AGAIN: if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); - if (m_imgui->button("?")) { wxGetApp().CallAfter([]() { SlaGizmoHelpDialog help_dlg; @@ -835,18 +753,18 @@ RENDER_AGAIN: }); } - m_imgui->end(); - - if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode - m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); + if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); force_refresh = true; } - m_old_editing_state = m_editing_mode; + + m_imgui->end(); + if (remove_selected || remove_all) { force_refresh = false; m_parent.set_as_dirty(); - bool was_in_editing = m_editing_mode; + if (remove_all) { select_point(AllPoints); delete_selected_points(true); // true - delete regardless of locked status @@ -927,12 +845,11 @@ void GLGizmoHollow::on_set_state() // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; + m_new_hole_radius = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); - m_normal_cache.clear(); m_clipping_plane_distance = 0.f; // Release clippers and the AABB raycaster. m_object_clipper.reset(); @@ -951,27 +868,27 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_point_before_drag = m_editing_cache[m_hover_id]; + m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].m_pos; } else - m_point_before_drag = CacheEntry(); + m_hole_before_drag = Vec3f::Zero(); } void GLGizmoHollow::on_stop_dragging() { if (m_hover_id != -1) { - CacheEntry backup = m_editing_cache[m_hover_id]; + Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].m_pos; - if (m_point_before_drag.support_point.pos != Vec3f::Zero() // some point was touched - && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected + if (m_hole_before_drag != Vec3f::Zero() // some point was touched + && backup != m_hole_before_drag) // and it was moved, not just selected { - m_editing_cache[m_hover_id] = m_point_before_drag; + m_model_object->sla_drain_holes[m_hover_id].m_pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_editing_cache[m_hover_id] = backup; + m_model_object->sla_drain_holes[m_hover_id].m_pos = backup; } } - m_point_before_drag = CacheEntry(); + m_hole_before_drag = Vec3f::Zero(); } @@ -981,9 +898,8 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) ar(m_clipping_plane_distance, *m_clipping_plane, m_model_object_id, - m_new_point_head_diameter, - m_normal_cache, - m_editing_cache, + m_new_hole_radius, + m_selected, m_selection_empty ); } @@ -995,9 +911,8 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const ar(m_clipping_plane_distance, *m_clipping_plane, m_model_object_id, - m_new_point_head_diameter, - m_normal_cache, - m_editing_cache, + m_new_hole_radius, + m_selected, m_selection_empty ); } @@ -1006,63 +921,41 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const void GLGizmoHollow::select_point(int i) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: select_point called when out of editing mode!" << std::endl; - std::abort(); - } - if (i == AllPoints || i == NoPoints) { - for (auto& point_and_selection : m_editing_cache) - point_and_selection.selected = ( i == AllPoints ); + m_selected.assign(m_selected.size(), i == AllPoints); m_selection_empty = (i == NoPoints); if (i == AllPoints) - m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f; + m_new_hole_radius = m_model_object->sla_drain_holes[0].m_radius; } else { - m_editing_cache[i].selected = true; + while (size_t(i) >= m_selected.size()) + m_selected.push_back(false); + m_selected[i] = true; m_selection_empty = false; - m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f; + m_new_hole_radius = m_model_object->sla_drain_holes[i].m_radius; } } void GLGizmoHollow::unselect_point(int i) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: unselect_point called when out of editing mode!" << std::endl; - std::abort(); - } - - m_editing_cache[i].selected = false; + m_selected[i] = false; m_selection_empty = true; - for (const CacheEntry& ce : m_editing_cache) { - if (ce.selected) { + for (const bool sel : m_selected) { + if (sel) { m_selection_empty = false; break; } } } - -bool GLGizmoHollow::unsaved_changes() const -{ - if (m_editing_cache.size() != m_normal_cache.size()) - return true; - - for (size_t i=0; isla_drain_holes.size(), false); } - void GLGizmoHollow::update_clipping_plane(bool keep_normal) const { Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index afaaf659b..ce3d60f27 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -25,7 +25,7 @@ private: ObjectID m_model_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb - mutable double m_z_shift = 0.f; + mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); const float RenderPointScale = 1.f; @@ -46,27 +46,26 @@ private: class CacheEntry { public: CacheEntry() : - support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {} + drain_hole(sla::DrainHole()), selected(false) {} - CacheEntry(const sla::SupportPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero()) : - support_point(point), selected(sel), normal(norm) {} + CacheEntry(const sla::DrainHole& point, bool sel = false) : + drain_hole(point), selected(sel) {} bool operator==(const CacheEntry& rhs) const { - return (support_point == rhs.support_point); + return (drain_hole == rhs.drain_hole); } bool operator!=(const CacheEntry& rhs) const { return ! ((*this) == rhs); } - sla::SupportPoint support_point; + sla::DrainHole drain_hole; bool selected; // whether the point is selected - Vec3f normal; template void serialize(Archive & ar) { - ar(support_point, selected, normal); + ar(drain_hole, selected); } }; @@ -97,17 +96,14 @@ private: bool unsaved_changes() const; const TriangleMesh* mesh() const; - bool m_editing_mode = true; // Is editing mode active? - bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). - float m_new_point_head_diameter; // Size of a new point. - float m_new_cone_angle = 0.f; - float m_new_cone_height = 5.f; - CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited - float m_old_point_head_diameter = 0.; // the same + bool m_show_supports = true; + float m_new_hole_radius; // Size of a new hole. + float m_new_hole_height = 5.f; + float m_old_hole_radius = 0.; // undo/redo - so we know what state was edited + Vec3f m_hole_before_drag = Vec3f::Zero(); float m_minimal_point_distance_stash = 0.f; // and again float m_density_stash = 0.f; // and again - mutable std::vector m_editing_cache; // a support point and whether it is currently selected - std::vector m_normal_cache; // to restore after discarding changes or undo/redo + mutable std::vector m_selected; // which holes are currently selected float m_offset = 2.f; float m_adaptability = 1.f; @@ -148,7 +144,7 @@ protected: void on_set_hover_id() override { - if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) + if (int(m_model_object->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } void on_start_dragging() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index be200b56d..f4427638b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -750,20 +750,31 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (evt.GetEventType() == wxEVT_KEY_UP) { - if (m_current == SlaSupports) + if (m_current == SlaSupports || m_current == Hollow) { - GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + bool is_editing = true; + bool is_rectangle_dragging = false; + + if (m_current == SlaSupports) { + GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + is_editing = gizmo->is_in_editing_mode(); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } + else { + GLGizmoHollow* gizmo = dynamic_cast(get_current()); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::ShiftUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::AltUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } }