diff --git a/resources/images/confirm.svg b/resources/images/confirm.svg
new file mode 100644
index 000000000..31a82f6e9
--- /dev/null
+++ b/resources/images/confirm.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/images/confirm_dark.svg b/resources/images/confirm_dark.svg
new file mode 100644
index 000000000..31a82f6e9
--- /dev/null
+++ b/resources/images/confirm_dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index 1774eb28a..99ec6aaa3 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -171,6 +171,7 @@ namespace ImGui
const wchar_t UnfoldButtonIcon = 0x0815;
const wchar_t SphereButtonIcon = 0x0816;
const wchar_t GapFillIcon = 0x0817;
+ const wchar_t ConfirmIcon = 0x0818;
const wchar_t MinimalizeDarkButton = 0x081C;
const wchar_t MinimalizeHoverDarkButton = 0x081D;
@@ -185,6 +186,7 @@ namespace ImGui
const wchar_t HeightRangeDarkIcon = 0x0825;
const wchar_t SphereButtonDarkIcon = 0x0826;
const wchar_t GapFillDarkIcon = 0x0827;
+ const wchar_t ConfirmDarkIcon = 0x0828;
const wchar_t TextSearchIcon = 0x0828;
const wchar_t TextSearchCloseIcon = 0x0829;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 6b421b153..d7a1f41b9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -15,6 +15,7 @@
#include
#include
+#include
namespace Slic3r::GUI {
@@ -140,10 +141,18 @@ void GLGizmoPainterBase::render_cursor() const
}
}
}
- // Raycast and return if there's no hit.
- update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices);
- if (m_rr.mesh_id == -1)
- return;
+
+ if (m_is_cursor_in_imgui == false) {
+ // Raycast and return if there's no hit.
+ update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices);
+ }
+ else {
+ m_rr.mouse_position = m_parent.get_local_mouse_position();
+ }
+ if (m_rr.mesh_id == -1) {
+ m_is_cursor_in_imgui = false;
+ return;
+ }
if (m_tool_type == ToolType::BRUSH) {
if (m_cursor_type == TriangleSelector::SPHERE)
@@ -238,6 +247,21 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
glsafe(::glPopMatrix());
}
+Vec2i GLGizmoPainterBase::_3d_to_mouse(Vec3d pos_in_3d, const Camera &camera) const
+{
+ Matrix4d modelview = camera.get_view_matrix().matrix();
+ Matrix4d projection = camera.get_projection_matrix().matrix();
+ Vec4i viewport(camera.get_viewport().data());
+ auto screen_width = viewport(2);
+ auto screen_height = viewport(3);
+ Vec4d origin_ndc = projection * modelview * Vec4d(pos_in_3d[0], pos_in_3d[1], pos_in_3d[2], 1.0);
+ Vec4d standard_ndc = origin_ndc / origin_ndc.w();
+ Vec2i screen;
+ screen[0] = screen_width * (standard_ndc[0] + 1.0) / 2.0;
+ screen[1] = screen_height - screen_height * (standard_ndc[1] + 1.0) / 2.0;
+ return screen;
+}
+
// BBS
void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) const
{
@@ -246,8 +270,69 @@ void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) co
float max_z = (float)box.max.z();
float min_z = (float)box.min.z();
- float cursor_z = std::clamp((float)hit_world.z(), min_z, max_z);
- std::array zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, min_z, max_z) };
+ if (m_is_cursor_in_imgui == false) {
+ m_cursor_z = std::clamp((float) hit_world.z(), min_z, max_z);
+ m_height_start_z_in_imgui = m_cursor_z;
+ }
+ ImGuiWrapper &imgui = *wxGetApp().imgui();
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0);
+ ImGui::PushStyleColor(ImGuiCol_WindowBg, m_is_dark_mode ? ImVec4(38 / 255.0, 46 / 255.0, 48 / 255.0, 0.7) : ImVec4(255 / 255.0, 255 / 255.0, 255 / 255.0, 0.7));
+ ImGui::SetNextWindowFocus();
+ imgui.set_next_window_pos(m_height_start_pos[0], m_height_start_pos[1], ImGuiCond_Always, 0.0f, 0.0f);
+
+ imgui.begin(wxString("cursor_height_range"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration);
+ ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
+ ImGui::AlignTextToFramePadding();
+ ImGui::PushStyleColor(ImGuiCol_Text, m_is_dark_mode ? ImVec4(255 / 255.0, 255 / 255.0, 255 / 255.0, 0.7) : ImVec4(38 / 255.0, 46 / 255.0, 48 / 255.0, 0.7));
+ ImGui::TextUnformatted(_L("Bottom:").ToUTF8().data());
+ ImGui::SameLine();
+ ImGui::PushItemWidth(50);
+
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
+ ImGui::PushStyleColor(ImGuiCol_FrameBg, m_is_dark_mode ? ImVec4(38 / 255.0, 46 / 255.0, 48 / 255.0, 0.7) : ImVec4(255 / 255.0, 255 / 255.0, 255 / 255.0, 0.7));
+ ImGui::BBLInputDouble("##m_height_start_z_in_imgui", &m_height_start_z_in_imgui, 0.0f, 0.0f, "%.2f");
+ ImGui::PopStyleVar(2);
+ ImGui::PopStyleColor(1);//for ImGuiCol_FrameBg
+ ImGui::PopStyleColor(1);//for Text
+
+ ImGui::SameLine();
+ ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(38 / 255.0, 46 / 255.0, 48 / 255.0, 0.7) : ImVec4(255 / 255.0, 255 / 255.0, 255 / 255.0, 0.7));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(50 / 255.0f, 58 / 255.0f, 61 / 255.0f, 1.00f) : ImVec4(238 / 255.0, 238 / 255.0, 238 / 255.0, 1.00f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(107 / 255.0f, 107 / 255.0f, 107 / 255.0f, 1.00f) : ImVec4(206 / 255.0f, 206 / 255.0f, 206 / 255.0f, 1.00f));
+ bool btn_clicked = ImGui::Button(into_u8(m_is_dark_mode ? ImGui::ConfirmDarkIcon : ImGui::ConfirmIcon).c_str());
+ ImGui::PopStyleColor(3);
+
+ auto imgui_size = ImGui::GetWindowSize();
+ const Camera &camera = wxGetApp().plater()->get_camera();
+ auto screen_height = Vec4i(camera.get_viewport().data())(3);
+ if (m_rr.mouse_position[0] >= m_height_start_pos[0] && m_rr.mouse_position[0] <= m_height_start_pos[0] + imgui_size[0]
+ && m_rr.mouse_position[1] >= m_height_start_pos[1] && m_rr.mouse_position[1] <= m_height_start_pos[1] + imgui_size[1]) {
+ m_is_cursor_in_imgui = true;
+ m_cursor_z = m_height_start_z_in_imgui;
+ } else {
+ m_is_cursor_in_imgui = false;
+ ImGui::SetNextWindowFocus();
+ }
+ if (btn_clicked) {
+ if (m_cursor_z >= min_z && m_cursor_z + m_cursor_height <= max_z) {
+ m_is_set_height_start_z_by_imgui = true;
+ const_cast(*this).gizmo_event(SLAGizmoEventType::LeftDown, Vec2d(0, 0), false, false, false);
+ m_is_set_height_start_z_by_imgui = false;
+ }
+ m_rr.mesh_id = -1; // exit
+ }
+ imgui.set_requires_extra_frame();
+ imgui.end();
+ ImGui::PopStyleVar(3);
+ ImGui::PopStyleColor(1);
+
+ if (m_cursor_z <= min_z || m_cursor_z + m_cursor_height >= max_z) {
+ return;
+ }
+ std::array zs = {m_cursor_z, std::clamp(m_cursor_z + m_cursor_height, min_z, max_z)};
const Selection& selection = m_parent.get_selection();
const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
@@ -264,7 +349,7 @@ void GLGizmoPainterBase::render_cursor_height_range(const Transform3d& trafo) co
}
for (int i = 0; i < zs.size(); i++) {
- update_contours(vol_mesh, zs[i], max_z, min_z);
+ update_contours(vol_mesh, zs[i], max_z, min_z, m_is_cursor_in_imgui? false:(i == 0 ? true : false));
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
@@ -289,7 +374,12 @@ BoundingBoxf3 GLGizmoPainterBase::bounding_box() const
return ret;
}
-void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const
+struct ScreenPosSort {
+ Vec2i pos_screen;
+ Vec3d pos_3d;
+};
+
+void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z, bool update_height_start_pos) const
{
const Selection& selection = m_parent.get_selection();
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
@@ -314,6 +404,36 @@ void GLGizmoPainterBase::update_contours(const TriangleMesh& vol_mesh, float cur
slicing_params.trafo = Transform3d::Identity().matrix();
const Polygons polys = slice_mesh(m_cut_contours.mesh.its, cursor_z, slicing_params);
if (!polys.empty()) {
+ if (update_height_start_pos) {
+ const Camera &camera = wxGetApp().plater()->get_camera();
+ std::vector screen_pos_sorts;
+ for (size_t i = 0; i < polys.size(); i++) {
+ for (size_t j = 0; j < polys[i].points.size(); j++) {
+ Vec2d pt = unscale(polys[i].points[j]).cast();
+ Vec3d pos_3d(pt[0], pt[1], cursor_z);
+ Vec2i cur_screen_pos = _3d_to_mouse(pos_3d, camera);
+ if (cur_screen_pos[0] >= m_rr.mouse_position[0]) {
+ ScreenPosSort cur{cur_screen_pos, pos_3d};
+ screen_pos_sorts.emplace_back(cur);
+ }
+ }
+ }
+ std::sort(screen_pos_sorts.begin(), screen_pos_sorts.end(), [=](const ScreenPosSort &s1, const ScreenPosSort &s2) {
+ int threshold_x = 20;
+ int threshold_y = 20;
+ if (abs(s1.pos_screen.x() - s2.pos_screen.x()) < threshold_x && abs(s1.pos_screen.y() - s2.pos_screen.y()) > threshold_y) {
+ return abs(s1.pos_screen.y() - m_rr.mouse_position[1]) < abs(s2.pos_screen.y() - m_rr.mouse_position[1]);
+ } else {
+ return s1.pos_screen.x() > s2.pos_screen.x();
+ }
+ });
+ if(screen_pos_sorts.size() >= 1){
+ m_height_start_pos = screen_pos_sorts[0].pos_screen;
+ // make mouse to cover in a part of imgui
+ m_height_start_pos[0] -= 10;
+ m_height_start_pos[1] -= 10;
+ }
+ }
m_cut_contours.contours.init_from(polys, static_cast(cursor_z));
m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
}
@@ -466,18 +586,19 @@ std::vector GLGizmoPainterBase::get_pr
const std::vector& trafo_matrices) const
{
std::vector hit_triangles_by_mesh;
-
- const Camera& camera = wxGetApp().plater()->get_camera();
-
- // In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
- update_raycast_cache(mouse_position, camera, trafo_matrices);
- if (m_rr.mesh_id == -1)
- return hit_triangles_by_mesh;
-
- ProjectedMousePosition mesh_hit_point = { m_rr.hit, m_rr.mesh_id, m_rr.facet };
- float z_bot_world= (trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))).z();
- float z_top_world = z_bot_world+ m_cursor_height;
- hit_triangles_by_mesh.push_back({ z_bot_world, m_rr.mesh_id, size_t(m_rr.facet) });
+ float z_bot_world;
+ if (m_is_set_height_start_z_by_imgui == false) {
+ const Camera &camera = wxGetApp().plater()->get_camera();
+ // In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
+ update_raycast_cache(mouse_position, camera, trafo_matrices);
+ if (m_rr.mesh_id == -1) return hit_triangles_by_mesh;
+ ProjectedMousePosition mesh_hit_point = {m_rr.hit, m_rr.mesh_id, m_rr.facet};
+ z_bot_world = (trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))).z();
+ } else {
+ z_bot_world = m_height_start_z_in_imgui;
+ }
+ float z_top_world = z_bot_world + m_cursor_height;
+ hit_triangles_by_mesh.push_back({z_bot_world, m_rr.mesh_id, size_t(m_rr.facet)});
const Selection& selection = m_parent.get_selection();
const ModelObject* mo = m_c->selection_info()->model_object();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
index db56d5865..2bbab7887 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
@@ -366,10 +366,14 @@ private:
int instance_idx{ -1 };
};
mutable CutContours m_cut_contours;
-
+ mutable float m_cursor_z{0};
+ mutable double m_height_start_z_in_imgui{0};
+ mutable bool m_is_set_height_start_z_by_imgui{false};
+ mutable Vec2i m_height_start_pos{0, 0};
+ mutable bool m_is_cursor_in_imgui{false};
BoundingBoxf3 bounding_box() const;
- void update_contours(const TriangleMesh& vol_mesh, float cursor_z, float max_z, float min_z) const;
-
+ void update_contours(const TriangleMesh &vol_mesh, float cursor_z, float max_z, float min_z, bool update_height_start_pos) const;
+ Vec2i _3d_to_mouse(Vec3d pos_in_3d, const Camera &camera) const;
protected:
void on_set_state() override;
void on_start_dragging() override {}
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 8753daa30..fcf655944 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -63,6 +63,7 @@ static const std::map font_icons = {
{ImGui::TriangleButtonIcon , "triangle_paint" },
{ImGui::FillButtonIcon , "fill_paint" },
{ImGui::HeightRangeIcon , "height_range" },
+ {ImGui::ConfirmIcon , "confirm" },
{ImGui::GapFillIcon , "gap_fill" },
{ImGui::FoldButtonIcon , "im_fold" },
{ImGui::UnfoldButtonIcon , "im_unfold" },
@@ -79,6 +80,7 @@ static const std::map font_icons = {
{ImGui::TriangleButtonDarkIcon , "triangle_paint_dark" },
{ImGui::FillButtonDarkIcon , "fill_paint_dark" },
{ImGui::HeightRangeDarkIcon , "height_range_dark" },
+ {ImGui::ConfirmDarkIcon , "confirm_dark" },
{ImGui::GapFillDarkIcon , "gap_fill_dark" },
{ImGui::SphereButtonDarkIcon , "toolbar_modifier_sphere_dark" },