diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index b8c182ef4..1f8513ac2 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -24,6 +24,8 @@ namespace GUI {
 const double Camera::DefaultDistance = 1000.0;
 double Camera::FrustrumMinZSize = 50.0;
 double Camera::FrustrumZMargin = 10.0;
+double Camera::FovMinDeg = 5.0;
+double Camera::FovMaxDeg = 75.0;
 
 Camera::Camera()
     : phi(45.0f)
@@ -34,6 +36,7 @@ Camera::Camera()
     , m_theta(45.0f)
     , m_zoom(1.0)
     , m_distance(DefaultDistance)
+    , m_gui_scale(1.0)
     , m_view_matrix(Transform3d::Identity())
     , m_projection_matrix(Transform3d::Identity())
 {
@@ -148,6 +151,18 @@ bool Camera::select_view(const std::string& direction)
         return false;
 }
 
+double Camera::get_fov() const
+{
+    switch (m_type)
+    {
+    case Perspective:
+        return 2.0 * Geometry::rad2deg(std::atan(1.0 / m_projection_matrix.matrix()(1, 1)));
+    default:
+    case Ortho:
+        return 0.0;
+    };
+}
+
 void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
 {
     glsafe(::glViewport(0, 0, w, h));
@@ -174,17 +189,67 @@ void Camera::apply_view_matrix() const
 
 void Camera::apply_projection(const BoundingBoxf3& box) const
 {
-    m_frustrum_zs = calc_tight_frustrum_zs_around(box);
+    m_distance = DefaultDistance;
+    double w = 0.0;
+    double h = 0.0;
 
-    double w = (double)m_viewport[2];
-    double h = (double)m_viewport[3];
-
-    double two_zoom = 2.0 * m_zoom;
-    if (two_zoom != 0.0)
+    while (true)
     {
-        double inv_two_zoom = 1.0 / two_zoom;
-        w *= inv_two_zoom;
-        h *= inv_two_zoom;
+        m_frustrum_zs = calc_tight_frustrum_zs_around(box);
+
+        w = (double)m_viewport[2];
+        h = (double)m_viewport[3];
+
+        double two_zoom = 2.0 * m_zoom;
+        if (two_zoom != 0.0)
+        {
+            double inv_two_zoom = 1.0 / two_zoom;
+            w *= inv_two_zoom;
+            h *= inv_two_zoom;
+        }
+
+        switch (m_type)
+        {
+        default:
+        case Ortho:
+        {
+            m_gui_scale = 1.0;
+            break;
+        }
+        case Perspective:
+        {
+            // scale near plane to keep w and h constant on the plane at z = m_distance
+            double scale = m_frustrum_zs.first / m_distance;
+            w *= scale;
+            h *= scale;
+            m_gui_scale = scale;
+            break;
+        }
+        }
+
+        if (m_type == Perspective)
+        {
+            double fov_rad = 2.0 * std::atan(h / m_frustrum_zs.first);
+            double fov_deg = Geometry::rad2deg(fov_rad);
+
+            // adjust camera distance to keep fov in a limited range
+            if (fov_deg > FovMaxDeg + 0.001)
+            {
+                double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMaxDeg));
+                m_distance += (new_near_z - m_frustrum_zs.first);
+                apply_view_matrix();
+            }
+            else if (fov_deg < FovMinDeg - 0.001)
+            {
+                double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMinDeg));
+                m_distance += (new_near_z - m_frustrum_zs.first);
+                apply_view_matrix();
+            }
+            else
+                break;
+        }
+        else
+            break;
     }
 
     glsafe(::glMatrixMode(GL_PROJECTION));
@@ -231,17 +296,22 @@ void Camera::debug_render() const
     std::string type = get_type_as_string();
     Vec3f position = get_position().cast<float>();
     Vec3f target = m_target.cast<float>();
+    float distance = (float)get_distance();
     Vec3f forward = get_dir_forward().cast<float>();
     Vec3f right = get_dir_right().cast<float>();
     Vec3f up = get_dir_up().cast<float>();
     float nearZ = (float)m_frustrum_zs.first;
     float farZ = (float)m_frustrum_zs.second;
     float deltaZ = farZ - nearZ;
+    float zoom = (float)m_zoom;
+    float fov = (float)get_fov();
+    float gui_scale = (float)get_gui_scale();
 
     ImGui::InputText("Type", const_cast<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
     ImGui::Separator();
     ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
     ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
+    ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
     ImGui::Separator();
     ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
     ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
@@ -250,6 +320,11 @@ void Camera::debug_render() const
     ImGui::InputFloat("Near Z", &nearZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
     ImGui::InputFloat("Far Z", &farZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
     ImGui::InputFloat("Delta Z", &deltaZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
+    ImGui::Separator();
+    ImGui::InputFloat("Zoom", &zoom, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
+    ImGui::InputFloat("Fov", &fov, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
+    ImGui::Separator();
+    ImGui::InputFloat("GUI scale", &gui_scale, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
     imgui.end();
 }
 #endif // ENABLE_CAMERA_STATISTICS
@@ -273,11 +348,10 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
     vertices.push_back(bb_max);
     vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
 
-    // set the Z range in eye coordinates (only negative Zs are in front of the camera)
+    // set the Z range in eye coordinates (negative Zs are in front of the camera)
     for (const Vec3d& v : vertices)
     {
-        // ensure non-negative values
-        double z = std::max(-(m_view_matrix * v)(2), 0.0);
+        double z = -(m_view_matrix * v)(2);
         ret.first = std::min(ret.first, z);
         ret.second = std::max(ret.second, z);
     }
@@ -295,8 +369,6 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
         ret.second = mid_z + half_size;
     }
 
-    assert(ret.first > 0.0);
-
     return ret;
 }
 
diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp
index 6a2f65a30..bd2541ce2 100644
--- a/src/slic3r/GUI/Camera.hpp
+++ b/src/slic3r/GUI/Camera.hpp
@@ -12,6 +12,8 @@ struct Camera
     static const double DefaultDistance;
     static double FrustrumMinZSize;
     static double FrustrumZMargin;
+    static double FovMinDeg;
+    static double FovMaxDeg;
 
     enum EType : unsigned char
     {
@@ -31,7 +33,8 @@ private:
     float m_theta;
     double m_zoom;
     // Distance between camera position and camera target measured along the camera Z axis
-    double m_distance;
+    mutable double m_distance;
+    mutable double m_gui_scale;
 
     mutable std::array<int, 4> m_viewport;
     mutable Transform3d m_view_matrix;
@@ -52,13 +55,14 @@ public:
     const Vec3d& get_target() const { return m_target; }
     void set_target(const Vec3d& target);
 
+    double get_distance() const { return m_distance; }
+    double get_gui_scale() const { return m_gui_scale; }
+
     float get_theta() const { return m_theta; }
     void set_theta(float theta, bool apply_limit);
 
     double get_zoom() const { return m_zoom; }
     void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
-    // this method does not check if the given zoom is valid, use instead the other set_zoom() method
-    // called only by: void GLCanvas3D::update_ui_from_settings()
     void set_zoom(double zoom) { m_zoom = zoom; }
 
     const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
@@ -79,6 +83,8 @@ public:
     double get_near_z() const { return m_frustrum_zs.first; }
     double get_far_z() const { return m_frustrum_zs.second; }
 
+    double get_fov() const;
+
     void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
     void apply_view_matrix() const;
     void apply_projection(const BoundingBoxf3& box) const;
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index d9a8a870c..2bb284fd4 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -3902,8 +3902,11 @@ void GLCanvas3D::_render_overlays() const
     glsafe(::glDisable(GL_DEPTH_TEST));
     glsafe(::glPushMatrix());
     glsafe(::glLoadIdentity());
-    // ensure the textures are renderered inside the frustrum
+    // ensure that the textures are renderered inside the frustrum
     glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.5)));
+    // ensure that the overlay fits the frustrum near z plane
+    double gui_scale = m_camera.get_gui_scale();
+    glsafe(::glScaled(gui_scale, gui_scale, 1.0));
 
     _render_gizmos_overlay();
     _render_warning_texture();