diff --git a/src/imguizmo/ImGuizmo.cpp b/src/imguizmo/ImGuizmo.cpp index 1acf8b63e45..c8e2ddb183f 100644 --- a/src/imguizmo/ImGuizmo.cpp +++ b/src/imguizmo/ImGuizmo.cpp @@ -31,6 +31,10 @@ #include #include "ImGuizmo.h" +#include +#include +#include + #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif @@ -678,16 +682,19 @@ namespace IMGUIZMO_NAMESPACE struct Context { +#if 0 Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) { } - +#endif ImDrawList* mDrawList; Style mStyle; - +#if 0 MODE mMode; +#endif matrix_t mViewMat; matrix_t mProjectionMat; +#if 0 matrix_t mModel; matrix_t mModelLocal; // orthonormalized model matrix_t mModelInverse; @@ -702,9 +709,10 @@ namespace IMGUIZMO_NAMESPACE vec_t mCameraRight; vec_t mCameraDir; vec_t mCameraUp; +#endif vec_t mRayOrigin; vec_t mRayVector; - +#if 0 float mRadiusSquareCenter; ImVec2 mScreenSquareCenter; ImVec2 mScreenSquareMin; @@ -715,9 +723,10 @@ namespace IMGUIZMO_NAMESPACE bool mbUsing; bool mbEnable; +#endif bool mbMouseOver; bool mReversed; // reversed projection matrix - +#if 0 // translation vec_t mTranslationPlan; vec_t mTranslationPlanOrigin; @@ -773,11 +782,13 @@ namespace IMGUIZMO_NAMESPACE bool mAllowAxisFlip = true; float mGizmoSizeClipSpace = 0.1f; +#endif }; static Context gContext; static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) }; +#if 0 static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" }; @@ -795,7 +806,7 @@ namespace IMGUIZMO_NAMESPACE static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion); static int GetRotateType(OPERATION op); static int GetScaleType(OPERATION op); - +#endif Style& GetStyle() { return gContext.mStyle; @@ -807,7 +818,7 @@ namespace IMGUIZMO_NAMESPACE return ImGui::ColorConvertFloat4ToU32(gContext.mStyle.Colors[idx]); } - static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) + static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(0, 0), ImVec2 size = ImVec2(0, 0)) { vec_t trans; trans.TransformPoint(worldPos, mat); @@ -821,7 +832,7 @@ namespace IMGUIZMO_NAMESPACE return ImVec2(trans.x, trans.y); } - static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) + static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(0, 0), ImVec2 size = ImVec2(0, 0)) { ImGuiIO& io = ImGui::GetIO(); @@ -841,7 +852,7 @@ namespace IMGUIZMO_NAMESPACE rayEnd *= 1.f / rayEnd.w; rayDir = Normalized(rayEnd - rayOrigin); } - +#if 0 static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false) { vec_t startOfSegment = start; @@ -911,7 +922,7 @@ namespace IMGUIZMO_NAMESPACE return vertPos1 + V * t; } - +#endif static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan) { const float numer = plan.Dot3(rOrigin) - plan.w; @@ -924,7 +935,7 @@ namespace IMGUIZMO_NAMESPACE return -(numer / denom); } - +#if 0 static float DistanceToPlane(const vec_t& point, const vec_t& plan) { return plan.Dot3(point) + plan.w; @@ -934,7 +945,7 @@ namespace IMGUIZMO_NAMESPACE { return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax); } - +#endif static bool IsHoveringWindow() { ImGuiContext& g = *ImGui::GetCurrentContext(); @@ -947,7 +958,7 @@ namespace IMGUIZMO_NAMESPACE return true; return false; } - +#if 0 void SetRect(float x, float y, float width, float height) { gContext.mX = x; @@ -973,7 +984,7 @@ namespace IMGUIZMO_NAMESPACE { ImGui::SetCurrentContext(ctx); } - +#endif void BeginFrame() { const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus; @@ -997,7 +1008,7 @@ namespace IMGUIZMO_NAMESPACE ImGui::PopStyleVar(); ImGui::PopStyleColor(2); } - +#if 0 bool IsUsing() { return (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) || gContext.mbUsingBounds; @@ -1045,14 +1056,16 @@ namespace IMGUIZMO_NAMESPACE gContext.mbUsingBounds = false; } } - +#endif static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode) { +#if 0 gContext.mMode = mode; +#endif gContext.mViewMat = *(matrix_t*)view; gContext.mProjectionMat = *(matrix_t*)projection; gContext.mbMouseOver = IsHoveringWindow(); - +#if 0 gContext.mModelLocal = *(matrix_t*)matrix; gContext.mModelLocal.OrthoNormalize(); @@ -1079,14 +1092,14 @@ namespace IMGUIZMO_NAMESPACE gContext.mCameraEye = viewInverse.v.position; gContext.mCameraRight = viewInverse.v.right; gContext.mCameraUp = viewInverse.v.up; - +#endif // projection reverse vec_t nearPos, farPos; nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat); farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat); gContext.mReversed = (nearPos.z/nearPos.w) > (farPos.z / farPos.w); - +#if 0 // compute scale from the size of camera right vector projected on screen at the matrix position vec_t pointRight = viewInverse.v.right; pointRight.TransformPoint(gContext.mViewProjection); @@ -1101,10 +1114,10 @@ namespace IMGUIZMO_NAMESPACE gContext.mScreenSquareCenter = centerSSpace; gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f); gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f); - +#endif ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector); } - +#if 0 static void ComputeColors(ImU32* colors, int type, OPERATION operation) { if (gContext.mbEnable) @@ -2775,8 +2788,8 @@ namespace IMGUIZMO_NAMESPACE } } } - - bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) +#endif + ViewManipulateResult ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) { // Scale is always local or matrix will be skewed when applying world scale or oriented matrix ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); @@ -2803,7 +2816,7 @@ namespace IMGUIZMO_NAMESPACE m16[15] = 1.0f; } - bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) + ViewManipulateResult ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) { static bool isDraging = false; static bool isClicking = false; @@ -2933,6 +2946,7 @@ namespace IMGUIZMO_NAMESPACE overBox = boxCoordInt; isClicking = true; isDraging = true; + interpolationFrames = 0; } } } @@ -3039,7 +3053,7 @@ namespace IMGUIZMO_NAMESPACE } } - bool viewUpdated = false; + ViewManipulateResult result; if (interpolationFrames) { interpolationFrames--; @@ -3053,7 +3067,7 @@ namespace IMGUIZMO_NAMESPACE newUp = interpolationUp; vec_t newEye = camTarget + newDir * length; LookAt(&newEye.x, &camTarget.x, &newUp.x, view); - viewUpdated = true; + result.changed = true; } isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos); @@ -3081,68 +3095,70 @@ namespace IMGUIZMO_NAMESPACE if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f) { - vec_t right = viewInverse.v.right; - if (fabsf(right.x) > fabsf(right.z)) - { - right.z = 0.f; - } - else - { - right.x = 0.f; - } - right.Normalize(); - interpolationUp = Cross(interpolationDir, right); - interpolationUp.Normalize(); + interpolationUp = overBox == 10 ? makeVect(1.f, 0.f, 0.f) : makeVect(-1.f, 0.f, 0.f); } else { interpolationUp = referenceUp; } interpolationFrames = 40; - + result.changed = true; + result.clicked_box = overBox; } isClicking = false; isDraging = false; } - if (isDraging) + if (isDraging && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1]))) { - matrix_t rx, ry, roll; + auto delta_x = io.MouseDelta.y * 0.01f; + auto delta_y = io.MouseDelta.x * 0.01f; - rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f); - ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f); + matrix_t vvv = *(matrix_t*)view; + // Calculate the rotation along x-axis + auto rot_x_deg = std::acos(std::clamp(Dot(vvv.v.up, referenceUp), -1.0f, 1.0f)); + if (vvv.v.up.z < 0) rot_x_deg *= -1; - roll = rx * ry; + const vec_t referenceRight = makeVect(1.f, 0.f, 0.f); + const vec_t referenceForward = makeVect(0.f, 0.f, 1.f); + matrix_t rx2; + rx2.RotationAxis(referenceRight, rot_x_deg); + vec_t f2; + f2.TransformVector(referenceForward, rx2); - vec_t newDir = viewInverse.v.dir; - newDir.TransformVector(roll); - newDir.Normalize(); + // Then calculate the rotation along y-axis + auto rot_y_deg = std::acos(std::clamp(Dot(vvv.v.dir, f2), -1.0f, 1.0f)); + if (vvv.v.dir.x < 0) rot_y_deg *= -1; - // clamp - vec_t planDir = Cross(viewInverse.v.right, referenceUp); - planDir.y = 0.f; - planDir.Normalize(); - float dt = Dot(planDir, newDir); - if (dt < 0.0f) - { - newDir += planDir * dt; - newDir.Normalize(); - } + // Apply deltas + rot_x_deg += delta_x; + rot_y_deg += delta_y; + // Clamp + if (rot_x_deg > 0.5 * M_PI) rot_x_deg = 0.5 * M_PI; + else if (rot_x_deg < -0.5 * M_PI) rot_x_deg = -0.5 * M_PI; - vec_t newEye = camTarget + newDir * length; - LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view); + matrix_t rx, ry, roll; + // Calculate new rotation matrix + rx.RotationAxis(referenceRight, rot_x_deg); + f2.TransformVector(referenceUp, rx); + ry.RotationAxis(f2, rot_y_deg); + + roll = rx * ry; + + *(matrix_t*)view = roll; #if IMGUI_VERSION_NUM >= 18723 ImGui::SetNextFrameWantCaptureMouse(true); #else ImGui::CaptureMouseFromApp(); #endif - viewUpdated = true; + result.changed = true; + result.dragging = true; } // restore view/projection because it was used to compute ray - ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode); + ComputeContext(svgView.m16, svgProjection.m16, nullptr/*gContext.mModelSource.m16*/, WORLD/*gContext.mMode*/); - return viewUpdated; + return result; } }; diff --git a/src/imguizmo/ImGuizmo.h b/src/imguizmo/ImGuizmo.h index 7f20bc13c28..23c41f6c347 100644 --- a/src/imguizmo/ImGuizmo.h +++ b/src/imguizmo/ImGuizmo.h @@ -206,15 +206,23 @@ namespace IMGUIZMO_NAMESPACE }; IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); + + struct ViewManipulateResult + { + bool changed = false; + bool dragging = false; + int clicked_box = -1; + }; + // // Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en // It seems to be a defensive patent in the US. I don't think it will bring troubles using it as // other software are using the same mechanics. But just in case, you are now warned! // - IMGUI_API bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); + IMGUI_API ViewManipulateResult ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); // use this version if you did not call Manipulate before and you are just using ViewManipulate - IMGUI_API bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); + IMGUI_API ViewManipulateResult ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); IMGUI_API void SetID(int id); diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 39520649a19..82427207a3f 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -175,6 +175,9 @@ void AppConfig::set_defaults() if (get("use_perspective_camera").empty()) set_bool("use_perspective_camera", true); + if (get("auto_perspective").empty()) + set_bool("auto_perspective", false); + if (get("use_free_camera").empty()) set_bool("use_free_camera", false); diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index c87b3cf2d2a..fd18a1cefdd 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -37,6 +37,7 @@ void Camera::set_type(EType type) { if (m_type != type && (type == EType::Ortho || type == EType::Perspective)) { m_type = type; + m_prevent_auto_type = true; if (m_update_config_on_type_change_enabled) { wxGetApp().app_config->set_bool("use_perspective_camera", m_type == EType::Perspective); } @@ -52,6 +53,20 @@ void Camera::select_next_type() set_type((EType)next); } +void Camera::auto_type(EType preferred_type) +{ + if (!wxGetApp().app_config->get_bool("auto_perspective")) return; + if (preferred_type == EType::Perspective) { + if (!m_prevent_auto_type) { + set_type(preferred_type); + m_prevent_auto_type = false; + } + } else { + set_type(preferred_type); + m_prevent_auto_type = false; + } +} + void Camera::translate(const Vec3d& displacement) { if (!displacement.isApprox(Vec3d::Zero())) { m_view_matrix.translate(-displacement); @@ -85,24 +100,41 @@ void Camera::set_zoom(double zoom) void Camera::select_view(const std::string& direction) { - if (direction == "iso") + if (direction == "iso") { set_default_orientation(); - else if (direction == "left") + auto_type(EType::Perspective); + } + else if (direction == "left") { look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); - else if (direction == "right") + auto_type(EType::Ortho); + } + else if (direction == "right") { look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ()); - else if (direction == "top") + auto_type(EType::Ortho); + } + else if (direction == "top") { look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY()); - else if (direction == "bottom") + auto_type(EType::Ortho); + } + else if (direction == "bottom") { look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY()); - else if (direction == "front") + auto_type(EType::Ortho); + } + else if (direction == "front") { look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); - else if (direction == "rear") + auto_type(EType::Ortho); + } + else if (direction == "rear") { look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); - else if (direction == "topfront") + auto_type(EType::Ortho); + } + else if (direction == "topfront") { look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ()); + auto_type(EType::Perspective); + } else if (direction == "plate") { look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ()); + auto_type(EType::Perspective); } } diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 328e8aa1a6d..48990f593c4 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -38,6 +38,7 @@ struct Camera int requires_zoom_to_plate{ REQUIRES_ZOOM_TO_PLATE_IDLE }; private: + bool m_prevent_auto_type = true; EType m_type{ EType::Perspective }; bool m_update_config_on_type_change_enabled{ false }; Vec3d m_target{ Vec3d::Zero() }; @@ -65,6 +66,7 @@ struct Camera // valid values for type: "false" -> ortho, "true" -> perspective void set_type(const std::string& type) { set_type((type == "true") ? EType::Perspective : EType::Ortho); } void select_next_type(); + void auto_type(EType preferred_type); void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index abcba5d4022..ae6497bea4a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3922,6 +3922,7 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt) camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center()); else camera.rotate_on_sphere(-rotate, 0, rotate_limit); + camera.auto_type(Camera::EType::Perspective); } m_dirty = true; } @@ -4285,10 +4286,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Orca: Sphere rotation for painting view // if dragging over blank area with left button, rotate if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { + Camera& camera = wxGetApp().plater()->get_camera(); const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports || m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { - Camera& camera = wxGetApp().plater()->get_camera(); Vec3d rotate_target = Vec3d::Zero(); if (!m_selection.is_empty()) rotate_target = m_selection.get_bounding_box().center(); @@ -4299,14 +4300,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else { if (wxGetApp().app_config->get_bool("use_free_camera")) // Virtual track ball (similar to the 3DConnexion mouse). - wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); + camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); else { // Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation. // It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(), // which checks an atomics (flushes CPU caches). // See GH issue #3816. - Camera& camera = wxGetApp().plater()->get_camera(); - bool rotate_limit = current_printer_technology() != ptSLA; camera.recover_from_free_camera(); @@ -4340,6 +4339,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } + camera.auto_type(Camera::EType::Perspective); m_dirty = true; } @@ -5675,7 +5675,6 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo return settings_changed; } -static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}; static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f}; void GLCanvas3D::_render_3d_navigator() @@ -5685,7 +5684,6 @@ void GLCanvas3D::_render_3d_navigator() } ImGuizmo::BeginFrame(); - ImGuizmo::AllowAxisFlip(false); auto& style = ImGuizmo::GetStyle(); style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y()); @@ -5713,7 +5711,6 @@ void GLCanvas3D::_render_3d_navigator() const float viewManipulateLeft = 0; const float viewManipulateTop = io.DisplaySize.y; const float camDistance = 8.f; - ImGuizmo::SetID(0); Camera& camera = wxGetApp().plater()->get_camera(); Transform3d m = Transform3d::Identity(); @@ -5729,11 +5726,11 @@ void GLCanvas3D::_render_3d_navigator() } const float size = 128 * sc; - const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD, - identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), - ImVec2(size, size), 0x00101010); + const auto result = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD, nullptr, + camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), ImVec2(size, size), + 0x00101010); - if (dirty) { + if (result.changed) { for (unsigned int c = 0; c < 4; ++c) { for (unsigned int r = 0; r < 4; ++r) { m(r, c) = cameraView[c * 4 + r]; @@ -5743,6 +5740,25 @@ void GLCanvas3D::_render_3d_navigator() m = m * (coord_mapping_transform.inverse()); camera.set_rotation(m); + if (result.dragging) { + // Switch back to perspective view when normal dragging + camera.auto_type(Camera::EType::Perspective); + } else if (result.clicked_box >= 0) { + switch (result.clicked_box) { + case 4: // back + case 10: // top + case 12: // right + case 14: // left + case 16: // bottom + case 22: // front + // Automatically switch to orthographic view when click the center face of the navigator + camera.auto_type(Camera::EType::Ortho); break; + default: + // Otherwise switch back to perspective view + camera.auto_type(Camera::EType::Perspective); break; + } + } + request_extra_frame(); } } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 945e8b493e3..e7f9a260ec1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2676,10 +2676,19 @@ void MainFrame::init_menubar_as_editor() wxGetApp().app_config->set_bool("use_perspective_camera", false); wxGetApp().update_ui_from_settings(); }, nullptr); - if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0) - viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true); - else - viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true); + this->Bind(wxEVT_UPDATE_UI, [viewMenu, camera_id_base](wxUpdateUIEvent& evt) { + if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0) + viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true); + else + viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true); + }, wxID_ANY); + append_menu_check_item(viewMenu, wxID_ANY, _L("Auto Perspective"), _L("Automatically switch between orthographic and perspective when changing from top/bottom/side views"), + [this](wxCommandEvent&) { + wxGetApp().app_config->set_bool("auto_perspective", !wxGetApp().app_config->get_bool("auto_perspective")); + m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); + }, + this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; }, + [this]() { return wxGetApp().app_config->get_bool("auto_perspective"); }, this); viewMenu->AppendSeparator(); append_menu_check_item(viewMenu, wxID_ANY, _L("Show &G-code Window") + "\tC", _L("Show g-code window in Preview scene"),