Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2019-06-24 16:27:43 +0300
committerbubnikv <bubnikv@gmail.com>2019-06-24 16:27:43 +0300
commit5320ed9374d4559ac0fcc5a299bd79018ed9a1cb (patch)
treea130db0c7adfed8b5cf2ce63645387da36cf5927 /src/slic3r/GUI/Camera.cpp
parent198600543dd707a51f29660c2583459ae2b34933 (diff)
parente737f15bfa68236d184dfba5861a3389fb903986 (diff)
Merge branch 'master' of https://github.com/Prusa3d/PrusaSlicer
Diffstat (limited to 'src/slic3r/GUI/Camera.cpp')
-rw-r--r--src/slic3r/GUI/Camera.cpp339
1 files changed, 322 insertions, 17 deletions
diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index dd6cbefe1..1f8513ac2 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -2,6 +2,8 @@
#include "Camera.hpp"
#include "3DScene.hpp"
+#include "GUI_App.hpp"
+#include "AppConfig.hpp"
#include <GL/glew.h>
@@ -19,32 +21,71 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
namespace Slic3r {
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()
- : type(Ortho)
- , zoom(1.0f)
- , phi(45.0f)
-// , distance(0.0f)
+ : phi(45.0f)
, requires_zoom_to_bed(false)
, inverted_phi(false)
- , m_theta(45.0f)
+ , m_type(Ortho)
, m_target(Vec3d::Zero())
+ , 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())
{
}
std::string Camera::get_type_as_string() const
{
- switch (type)
+ switch (m_type)
{
- default:
case Unknown:
return "unknown";
-// case Perspective:
-// return "perspective";
+ case Perspective:
+ return "perspective";
+ default:
case Ortho:
- return "ortho";
+ return "orthographic";
};
}
+void Camera::set_type(EType type)
+{
+ if (m_type != type)
+ {
+ m_type = type;
+
+ wxGetApp().app_config->set("camera_type", std::to_string(m_type));
+ wxGetApp().app_config->save();
+ }
+}
+
+void Camera::set_type(const std::string& type)
+{
+ if (!type.empty() && (type != "1"))
+ {
+ unsigned char type_id = atoi(type.c_str());
+ if (((unsigned char)Ortho < type_id) && (type_id < (unsigned char)Num_types))
+ set_type((Camera::EType)type_id);
+ }
+}
+
+void Camera::select_next_type()
+{
+ unsigned char next = (unsigned char)m_type + 1;
+ if (next == (unsigned char)Num_types)
+ next = 1;
+
+ set_type((EType)next);
+}
+
void Camera::set_target(const Vec3d& target)
{
m_target = target;
@@ -65,9 +106,20 @@ void Camera::set_theta(float theta, bool apply_limit)
}
}
-void Camera::set_scene_box(const BoundingBoxf3& box)
+void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
{
- m_scene_box = box;
+ zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0;
+ zoom = m_zoom / (1.0 - zoom);
+
+ // Don't allow to zoom too far outside the scene.
+ double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h);
+ if (zoom_min > 0.0)
+ zoom = std::max(zoom, zoom_min * 0.7);
+
+ // Don't allow to zoom too close to the scene.
+ zoom = std::min(zoom, 100.0);
+
+ m_zoom = zoom;
}
bool Camera::select_view(const std::string& direction)
@@ -99,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));
@@ -107,27 +171,268 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
void Camera::apply_view_matrix() const
{
+ double theta_rad = Geometry::deg2rad(-(double)m_theta);
+ double phi_rad = Geometry::deg2rad((double)phi);
+ double sin_theta = ::sin(theta_rad);
+ Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
+
glsafe(::glMatrixMode(GL_MODELVIEW));
glsafe(::glLoadIdentity());
glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch
- glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
- glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2)));
+ glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
+
+ glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2)));
glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data()));
}
-void Camera::apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const
+void Camera::apply_projection(const BoundingBoxf3& box) const
{
+ m_distance = DefaultDistance;
+ double w = 0.0;
+ double h = 0.0;
+
+ while (true)
+ {
+ 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));
glsafe(::glLoadIdentity());
- glsafe(::glOrtho(x_min, x_max, y_min, y_max, z_min, z_max));
- glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
+ switch (m_type)
+ {
+ default:
+ case Ortho:
+ {
+ glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
+ break;
+ }
+ case Perspective:
+ {
+ glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
+ break;
+ }
+ }
+ glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
glsafe(::glMatrixMode(GL_MODELVIEW));
}
+void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
+{
+ // Calculate the zoom factor needed to adjust the view around the given box.
+ double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h);
+ if (zoom > 0.0)
+ {
+ m_zoom = zoom;
+ // center view around box center
+ m_target = box.center();
+ }
+}
+
+#if ENABLE_CAMERA_STATISTICS
+void Camera::debug_render() const
+{
+ ImGuiWrapper& imgui = *wxGetApp().imgui();
+ imgui.set_next_window_bg_alpha(0.5f);
+ imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+ 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);
+ ImGui::InputFloat3("Up", up.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
+ ImGui::Separator();
+ 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
+
+std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
+{
+ std::pair<double, double> ret = std::make_pair(DBL_MAX, -DBL_MAX);
+
+ Vec3d bb_min = box.min;
+ Vec3d bb_max = box.max;
+
+ // box vertices in world space
+ std::vector<Vec3d> vertices;
+ vertices.reserve(8);
+ vertices.push_back(bb_min);
+ vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2));
+ vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2));
+ vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2));
+ vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2));
+ vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2));
+ vertices.push_back(bb_max);
+ vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
+
+ // set the Z range in eye coordinates (negative Zs are in front of the camera)
+ for (const Vec3d& v : vertices)
+ {
+ double z = -(m_view_matrix * v)(2);
+ ret.first = std::min(ret.first, z);
+ ret.second = std::max(ret.second, z);
+ }
+
+ // apply margin
+ ret.first -= FrustrumZMargin;
+ ret.second += FrustrumZMargin;
+
+ // ensure min size
+ if (ret.second - ret.first < FrustrumMinZSize)
+ {
+ double mid_z = 0.5 * (ret.first + ret.second);
+ double half_size = 0.5 * FrustrumMinZSize;
+ ret.first = mid_z - half_size;
+ ret.second = mid_z + half_size;
+ }
+
+ return ret;
+}
+
+double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const
+{
+ double max_bb_size = box.max_size();
+ if (max_bb_size == 0.0)
+ return -1.0;
+
+ // project the box vertices on a plane perpendicular to the camera forward axis
+ // then calculates the vertices coordinate on this plane along the camera xy axes
+
+ // ensure that the view matrix is updated
+ apply_view_matrix();
+
+ Vec3d right = get_dir_right();
+ Vec3d up = get_dir_up();
+ Vec3d forward = get_dir_forward();
+
+ Vec3d bb_min = box.min;
+ Vec3d bb_max = box.max;
+ Vec3d bb_center = box.center();
+
+ // box vertices in world space
+ std::vector<Vec3d> vertices;
+ vertices.reserve(8);
+ vertices.push_back(bb_min);
+ vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2));
+ vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2));
+ vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2));
+ vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2));
+ vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2));
+ vertices.push_back(bb_max);
+ vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
+
+ double max_x = 0.0;
+ double max_y = 0.0;
+
+ // margin factor to give some empty space around the box
+ double margin_factor = 1.25;
+
+ for (const Vec3d& v : vertices)
+ {
+ // project vertex on the plane perpendicular to camera forward axis
+ Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2));
+ Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
+
+ // calculates vertex coordinate along camera xy axes
+ double x_on_plane = proj_on_plane.dot(right);
+ double y_on_plane = proj_on_plane.dot(up);
+
+ max_x = std::max(max_x, std::abs(x_on_plane));
+ max_y = std::max(max_y, std::abs(y_on_plane));
+ }
+
+ if ((max_x == 0.0) || (max_y == 0.0))
+ return -1.0f;
+
+ max_x *= margin_factor;
+ max_y *= margin_factor;
+
+ return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y));
+}
+
} // GUI
} // Slic3r