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:
Diffstat (limited to 'src/slic3r/GUI/Gizmos/GLGizmoCut.cpp')
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoCut.cpp832
1 files changed, 416 insertions, 416 deletions
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 9a87d5a45..dfad3817c 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -1,416 +1,416 @@
-// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
-#include "GLGizmoCut.hpp"
-#include "slic3r/GUI/GLCanvas3D.hpp"
-
-#include <GL/glew.h>
-
-#include <wx/button.h>
-#include <wx/checkbox.h>
-#include <wx/stattext.h>
-#include <wx/sizer.h>
-
-#include <algorithm>
-
-#include "slic3r/GUI/GUI_App.hpp"
-#include "slic3r/GUI/Plater.hpp"
-#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
-#include "libslic3r/AppConfig.hpp"
-#include "libslic3r/Model.hpp"
-#include "libslic3r/TriangleMeshSlicer.hpp"
-
-namespace Slic3r {
-namespace GUI {
-
-const double GLGizmoCut::Offset = 10.0;
-const double GLGizmoCut::Margin = 20.0;
-static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE();
-
-GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
- : GLGizmoBase(parent, icon_filename, sprite_id)
-{}
-
-std::string GLGizmoCut::get_tooltip() const
-{
- double cut_z = m_cut_z;
- if (wxGetApp().app_config->get("use_inches") == "1")
- cut_z *= ObjectManipulation::mm_to_in;
-
- return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(cut_z, 2) : "";
-}
-
-bool GLGizmoCut::on_mouse(const wxMouseEvent &mouse_event)
-{
- return use_grabbers(mouse_event);
-}
-
-bool GLGizmoCut::on_init()
-{
- m_grabbers.emplace_back();
- m_shortcut_key = WXK_CONTROL_C;
- return true;
-}
-
-std::string GLGizmoCut::on_get_name() const
-{
- return _u8L("Cut");
-}
-
-void GLGizmoCut::on_set_state()
-{
- // Reset m_cut_z on gizmo activation
- if (m_state == On)
- m_cut_z = bounding_box().center().z();
-}
-
-bool GLGizmoCut::on_is_activable() const
-{
- const Selection& selection = m_parent.get_selection();
- return selection.is_single_full_instance() && !selection.is_wipe_tower();
-}
-
-void GLGizmoCut::on_start_dragging()
-{
- if (m_hover_id == -1)
- return;
-
- const BoundingBoxf3 box = bounding_box();
- m_max_z = box.max.z();
- m_start_z = m_cut_z;
- m_drag_pos = m_grabbers[m_hover_id].center;
- m_drag_center = box.center();
- m_drag_center.z() = m_cut_z;
-}
-
-void GLGizmoCut::on_dragging(const UpdateData &data)
-{
- assert(m_hover_id != -1);
- set_cut_z(m_start_z + calc_projection(data.mouse_ray));
-}
-
-void GLGizmoCut::on_render()
-{
- const BoundingBoxf3 box = bounding_box();
- Vec3d plane_center = box.center();
- plane_center.z() = m_cut_z;
- m_max_z = box.max.z();
- set_cut_z(m_cut_z);
-
- update_contours();
-
- const float min_x = box.min.x() - Margin;
- const float max_x = box.max.x() + Margin;
- const float min_y = box.min.y() - Margin;
- const float max_y = box.max.y() + Margin;
- glsafe(::glEnable(GL_DEPTH_TEST));
- glsafe(::glDisable(GL_CULL_FACE));
- glsafe(::glEnable(GL_BLEND));
- glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
-
-#if ENABLE_LEGACY_OPENGL_REMOVAL
- GLShaderProgram* shader = wxGetApp().get_shader("flat");
- if (shader != nullptr) {
- shader->start_using();
- const Vec3d diff = plane_center - m_old_center;
- // Z changed when move with cut plane
- // X and Y changed when move with cutted object
- bool is_changed = std::abs(diff.x()) > EPSILON ||
- std::abs(diff.y()) > EPSILON ||
- std::abs(diff.z()) > EPSILON;
- m_old_center = plane_center;
-
- if (!m_plane.is_initialized() || is_changed) {
- m_plane.reset();
-
- GLModel::Geometry init_data;
- init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
- init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f };
- init_data.reserve_vertices(4);
- init_data.reserve_indices(6);
-
- // vertices
- init_data.add_vertex(Vec3f(min_x, min_y, plane_center.z()));
- init_data.add_vertex(Vec3f(max_x, min_y, plane_center.z()));
- init_data.add_vertex(Vec3f(max_x, max_y, plane_center.z()));
- init_data.add_vertex(Vec3f(min_x, max_y, plane_center.z()));
-
- // indices
- init_data.add_triangle(0, 1, 2);
- init_data.add_triangle(2, 3, 0);
-
- m_plane.init_from(std::move(init_data));
- }
-
-#if ENABLE_GL_SHADERS_ATTRIBUTES
- const Camera& camera = wxGetApp().plater()->get_camera();
- shader->set_uniform("view_model_matrix", camera.get_view_matrix());
- shader->set_uniform("projection_matrix", camera.get_projection_matrix());
-#endif // ENABLE_GL_SHADERS_ATTRIBUTES
-
- m_plane.render();
-#else
- // Draw the cutting plane
- ::glBegin(GL_QUADS);
- ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
- ::glVertex3f(min_x, min_y, plane_center.z());
- ::glVertex3f(max_x, min_y, plane_center.z());
- ::glVertex3f(max_x, max_y, plane_center.z());
- ::glVertex3f(min_x, max_y, plane_center.z());
- glsafe(::glEnd());
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-
- glsafe(::glEnable(GL_CULL_FACE));
- glsafe(::glDisable(GL_BLEND));
-
- // Draw the grabber and the connecting line
- m_grabbers[0].center = plane_center;
- m_grabbers[0].center.z() = plane_center.z() + Offset;
-
- glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
-
- glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
-#if ENABLE_LEGACY_OPENGL_REMOVAL
- if (!m_grabber_connection.is_initialized() || is_changed) {
- m_grabber_connection.reset();
-
- GLModel::Geometry init_data;
- init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
- init_data.color = ColorRGBA::YELLOW();
- init_data.reserve_vertices(2);
- init_data.reserve_indices(2);
-
- // vertices
- init_data.add_vertex((Vec3f)plane_center.cast<float>());
- init_data.add_vertex((Vec3f)m_grabbers[0].center.cast<float>());
-
- // indices
- init_data.add_line(0, 1);
-
- m_grabber_connection.init_from(std::move(init_data));
- }
-
- m_grabber_connection.render();
-
- shader->stop_using();
- }
-
- shader = wxGetApp().get_shader("gouraud_light");
-#else
- glsafe(::glColor3f(1.0, 1.0, 0.0));
- ::glBegin(GL_LINES);
- ::glVertex3dv(plane_center.data());
- ::glVertex3dv(m_grabbers[0].center.data());
- glsafe(::glEnd());
-
- GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
- if (shader != nullptr) {
- shader->start_using();
- shader->set_uniform("emission_factor", 0.1f);
-
- m_grabbers[0].color = GRABBER_COLOR;
- m_grabbers[0].render(m_hover_id == 0, float((box.size().x() + box.size().y() + box.size().z()) / 3.0));
-
- shader->stop_using();
- }
-
-#if ENABLE_LEGACY_OPENGL_REMOVAL
- shader = wxGetApp().get_shader("flat");
- if (shader != nullptr) {
- shader->start_using();
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-#if ENABLE_GL_SHADERS_ATTRIBUTES
- const Camera& camera = wxGetApp().plater()->get_camera();
- shader->set_uniform("view_model_matrix", camera.get_view_matrix()* Geometry::assemble_transform(m_cut_contours.shift));
- shader->set_uniform("projection_matrix", camera.get_projection_matrix());
-#else
- glsafe(::glPushMatrix());
- glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
-#endif // ENABLE_GL_SHADERS_ATTRIBUTES
- glsafe(::glLineWidth(2.0f));
- m_cut_contours.contours.render();
-#if !ENABLE_GL_SHADERS_ATTRIBUTES
- glsafe(::glPopMatrix());
-#endif // !ENABLE_GL_SHADERS_ATTRIBUTES
-#if ENABLE_LEGACY_OPENGL_REMOVAL
- shader->stop_using();
- }
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
- }
-
-void GLGizmoCut::on_render_for_picking()
-{
- glsafe(::glDisable(GL_DEPTH_TEST));
- render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
-}
-
-void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
-{
- static float last_y = 0.0f;
- static float last_h = 0.0f;
-
- m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
-
- const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
-
- // adjust window position to avoid overlap the view toolbar
- const float win_h = ImGui::GetWindowHeight();
- y = std::min(y, bottom_limit - win_h);
- ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
- if (last_h != win_h || last_y != y) {
- // ask canvas for another frame to render the window in the correct position
- m_imgui->set_requires_extra_frame();
- if (last_h != win_h)
- last_h = win_h;
- if (last_y != y)
- last_y = y;
- }
-
- ImGui::AlignTextToFramePadding();
- m_imgui->text("Z");
- ImGui::SameLine();
- ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f);
-
- double cut_z = m_cut_z;
- if (imperial_units)
- cut_z *= ObjectManipulation::mm_to_in;
- ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
-
- ImGui::SameLine();
- m_imgui->text(imperial_units ? _L("in") : _L("mm"));
-
- m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0);
-
- ImGui::Separator();
-
- m_imgui->checkbox(_L("Keep upper part"), m_keep_upper);
- m_imgui->checkbox(_L("Keep lower part"), m_keep_lower);
- m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower);
-
- ImGui::Separator();
-
- m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || m_cut_z <= 0.0 || m_max_z <= m_cut_z);
- const bool cut_clicked = m_imgui->button(_L("Perform cut"));
- m_imgui->disabled_end();
-
- m_imgui->end();
-
- if (cut_clicked && (m_keep_upper || m_keep_lower))
- perform_cut(m_parent.get_selection());
-}
-
-void GLGizmoCut::set_cut_z(double cut_z)
-{
- // Clamp the plane to the object's bounding box
- m_cut_z = std::clamp(cut_z, 0.0, m_max_z);
-}
-
-void GLGizmoCut::perform_cut(const Selection& selection)
-{
- const int instance_idx = selection.get_instance_idx();
- const int object_idx = selection.get_object_idx();
-
- wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
-
- // m_cut_z is the distance from the bed. Subtract possible SLA elevation.
- const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
- const double object_cut_z = m_cut_z - first_glvolume->get_sla_shift_z();
-
- if (0.0 < object_cut_z && object_cut_z < m_max_z)
- wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z,
- only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
- only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) |
- only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
- else {
- // the object is SLA-elevated and the plane is under it.
- }
-}
-
-double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const
-{
- double projection = 0.0;
-
- const Vec3d starting_vec = m_drag_pos - m_drag_center;
- const double len_starting_vec = starting_vec.norm();
- if (len_starting_vec != 0.0) {
- const Vec3d mouse_dir = mouse_ray.unit_vector();
- // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
- // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
- // in our case plane normal and ray direction are the same (orthogonal view)
- // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
- const Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
- // vector from the starting position to the found intersection
- const Vec3d inters_vec = inters - m_drag_pos;
-
- // finds projection of the vector along the staring direction
- projection = inters_vec.dot(starting_vec.normalized());
- }
- return projection;
-}
-
-BoundingBoxf3 GLGizmoCut::bounding_box() const
-{
- BoundingBoxf3 ret;
- const Selection& selection = m_parent.get_selection();
- const Selection::IndicesList& idxs = selection.get_volume_idxs();
- return selection.get_bounding_box();
-
- for (unsigned int i : idxs) {
- const GLVolume* volume = selection.get_volume(i);
- if (!volume->is_modifier)
- ret.merge(volume->transformed_convex_hull_bounding_box());
- }
- return ret;
-}
-
-void GLGizmoCut::update_contours()
-{
- const Selection& selection = m_parent.get_selection();
- const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
- const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box();
-
- const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
- const int instance_idx = selection.get_instance_idx();
- std::vector<ObjectID> volumes_idxs = std::vector<ObjectID>(model_object->volumes.size());
- for (size_t i = 0; i < model_object->volumes.size(); ++i) {
- volumes_idxs[i] = model_object->volumes[i]->id();
- }
-
- if (0.0 < m_cut_z && m_cut_z < m_max_z) {
- if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() ||
- m_cut_contours.instance_idx != instance_idx || m_cut_contours.volumes_idxs != volumes_idxs) {
- m_cut_contours.cut_z = m_cut_z;
-
- if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs)
- m_cut_contours.mesh = model_object->raw_mesh();
-
- m_cut_contours.position = box.center();
- m_cut_contours.shift = Vec3d::Zero();
- m_cut_contours.object_id = model_object->id();
- m_cut_contours.instance_idx = instance_idx;
- m_cut_contours.volumes_idxs = volumes_idxs;
- m_cut_contours.contours.reset();
-
- MeshSlicingParams slicing_params;
- slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix();
- slicing_params.trafo.pretranslate(Vec3d(0., 0., first_glvolume->get_sla_shift_z()));
-
- const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params);
- if (!polys.empty()) {
- m_cut_contours.contours.init_from(polys, static_cast<float>(m_cut_z));
-#if ENABLE_LEGACY_OPENGL_REMOVAL
- m_cut_contours.contours.set_color(ColorRGBA::WHITE());
-#else
- m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
- }
- }
- else if (box.center() != m_cut_contours.position) {
- m_cut_contours.shift = box.center() - m_cut_contours.position;
- }
- }
- else
- m_cut_contours.contours.reset();
-}
-
-} // namespace GUI
-} // namespace Slic3r
+// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
+#include "GLGizmoCut.hpp"
+#include "slic3r/GUI/GLCanvas3D.hpp"
+
+#include <GL/glew.h>
+
+#include <wx/button.h>
+#include <wx/checkbox.h>
+#include <wx/stattext.h>
+#include <wx/sizer.h>
+
+#include <algorithm>
+
+#include "slic3r/GUI/GUI_App.hpp"
+#include "slic3r/GUI/Plater.hpp"
+#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
+#include "libslic3r/AppConfig.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/TriangleMeshSlicer.hpp"
+
+namespace Slic3r {
+namespace GUI {
+
+const double GLGizmoCut::Offset = 10.0;
+const double GLGizmoCut::Margin = 20.0;
+static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE();
+
+GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
+ : GLGizmoBase(parent, icon_filename, sprite_id)
+{}
+
+std::string GLGizmoCut::get_tooltip() const
+{
+ double cut_z = m_cut_z;
+ if (wxGetApp().app_config->get("use_inches") == "1")
+ cut_z *= ObjectManipulation::mm_to_in;
+
+ return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(cut_z, 2) : "";
+}
+
+bool GLGizmoCut::on_mouse(const wxMouseEvent &mouse_event)
+{
+ return use_grabbers(mouse_event);
+}
+
+bool GLGizmoCut::on_init()
+{
+ m_grabbers.emplace_back();
+ m_shortcut_key = WXK_CONTROL_C;
+ return true;
+}
+
+std::string GLGizmoCut::on_get_name() const
+{
+ return _u8L("Cut");
+}
+
+void GLGizmoCut::on_set_state()
+{
+ // Reset m_cut_z on gizmo activation
+ if (m_state == On)
+ m_cut_z = bounding_box().center().z();
+}
+
+bool GLGizmoCut::on_is_activable() const
+{
+ const Selection& selection = m_parent.get_selection();
+ return selection.is_single_full_instance() && !selection.is_wipe_tower();
+}
+
+void GLGizmoCut::on_start_dragging()
+{
+ if (m_hover_id == -1)
+ return;
+
+ const BoundingBoxf3 box = bounding_box();
+ m_max_z = box.max.z();
+ m_start_z = m_cut_z;
+ m_drag_pos = m_grabbers[m_hover_id].center;
+ m_drag_center = box.center();
+ m_drag_center.z() = m_cut_z;
+}
+
+void GLGizmoCut::on_dragging(const UpdateData &data)
+{
+ assert(m_hover_id != -1);
+ set_cut_z(m_start_z + calc_projection(data.mouse_ray));
+}
+
+void GLGizmoCut::on_render()
+{
+ const BoundingBoxf3 box = bounding_box();
+ Vec3d plane_center = box.center();
+ plane_center.z() = m_cut_z;
+ m_max_z = box.max.z();
+ set_cut_z(m_cut_z);
+
+ update_contours();
+
+ const float min_x = box.min.x() - Margin;
+ const float max_x = box.max.x() + Margin;
+ const float min_y = box.min.y() - Margin;
+ const float max_y = box.max.y() + Margin;
+ glsafe(::glEnable(GL_DEPTH_TEST));
+ glsafe(::glDisable(GL_CULL_FACE));
+ glsafe(::glEnable(GL_BLEND));
+ glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+
+#if ENABLE_LEGACY_OPENGL_REMOVAL
+ GLShaderProgram* shader = wxGetApp().get_shader("flat");
+ if (shader != nullptr) {
+ shader->start_using();
+ const Vec3d diff = plane_center - m_old_center;
+ // Z changed when move with cut plane
+ // X and Y changed when move with cutted object
+ bool is_changed = std::abs(diff.x()) > EPSILON ||
+ std::abs(diff.y()) > EPSILON ||
+ std::abs(diff.z()) > EPSILON;
+ m_old_center = plane_center;
+
+ if (!m_plane.is_initialized() || is_changed) {
+ m_plane.reset();
+
+ GLModel::Geometry init_data;
+ init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
+ init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f };
+ init_data.reserve_vertices(4);
+ init_data.reserve_indices(6);
+
+ // vertices
+ init_data.add_vertex(Vec3f(min_x, min_y, plane_center.z()));
+ init_data.add_vertex(Vec3f(max_x, min_y, plane_center.z()));
+ init_data.add_vertex(Vec3f(max_x, max_y, plane_center.z()));
+ init_data.add_vertex(Vec3f(min_x, max_y, plane_center.z()));
+
+ // indices
+ init_data.add_triangle(0, 1, 2);
+ init_data.add_triangle(2, 3, 0);
+
+ m_plane.init_from(std::move(init_data));
+ }
+
+#if ENABLE_GL_SHADERS_ATTRIBUTES
+ const Camera& camera = wxGetApp().plater()->get_camera();
+ shader->set_uniform("view_model_matrix", camera.get_view_matrix());
+ shader->set_uniform("projection_matrix", camera.get_projection_matrix());
+#endif // ENABLE_GL_SHADERS_ATTRIBUTES
+
+ m_plane.render();
+#else
+ // Draw the cutting plane
+ ::glBegin(GL_QUADS);
+ ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
+ ::glVertex3f(min_x, min_y, plane_center.z());
+ ::glVertex3f(max_x, min_y, plane_center.z());
+ ::glVertex3f(max_x, max_y, plane_center.z());
+ ::glVertex3f(min_x, max_y, plane_center.z());
+ glsafe(::glEnd());
+#endif // ENABLE_LEGACY_OPENGL_REMOVAL
+
+ glsafe(::glEnable(GL_CULL_FACE));
+ glsafe(::glDisable(GL_BLEND));
+
+ // Draw the grabber and the connecting line
+ m_grabbers[0].center = plane_center;
+ m_grabbers[0].center.z() = plane_center.z() + Offset;
+
+ glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
+
+ glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
+#if ENABLE_LEGACY_OPENGL_REMOVAL
+ if (!m_grabber_connection.is_initialized() || is_changed) {
+ m_grabber_connection.reset();
+
+ GLModel::Geometry init_data;
+ init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
+ init_data.color = ColorRGBA::YELLOW();
+ init_data.reserve_vertices(2);
+ init_data.reserve_indices(2);
+
+ // vertices
+ init_data.add_vertex((Vec3f)plane_center.cast<float>());
+ init_data.add_vertex((Vec3f)m_grabbers[0].center.cast<float>());
+
+ // indices
+ init_data.add_line(0, 1);
+
+ m_grabber_connection.init_from(std::move(init_data));
+ }
+
+ m_grabber_connection.render();
+
+ shader->stop_using();
+ }
+
+ shader = wxGetApp().get_shader("gouraud_light");
+#else
+ glsafe(::glColor3f(1.0, 1.0, 0.0));
+ ::glBegin(GL_LINES);
+ ::glVertex3dv(plane_center.data());
+ ::glVertex3dv(m_grabbers[0].center.data());
+ glsafe(::glEnd());
+
+ GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
+#endif // ENABLE_LEGACY_OPENGL_REMOVAL
+ if (shader != nullptr) {
+ shader->start_using();
+ shader->set_uniform("emission_factor", 0.1f);
+
+ m_grabbers[0].color = GRABBER_COLOR;
+ m_grabbers[0].render(m_hover_id == 0, float((box.size().x() + box.size().y() + box.size().z()) / 3.0));
+
+ shader->stop_using();
+ }
+
+#if ENABLE_LEGACY_OPENGL_REMOVAL
+ shader = wxGetApp().get_shader("flat");
+ if (shader != nullptr) {
+ shader->start_using();
+#endif // ENABLE_LEGACY_OPENGL_REMOVAL
+#if ENABLE_GL_SHADERS_ATTRIBUTES
+ const Camera& camera = wxGetApp().plater()->get_camera();
+ shader->set_uniform("view_model_matrix", camera.get_view_matrix()* Geometry::assemble_transform(m_cut_contours.shift));
+ shader->set_uniform("projection_matrix", camera.get_projection_matrix());
+#else
+ glsafe(::glPushMatrix());
+ glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
+#endif // ENABLE_GL_SHADERS_ATTRIBUTES
+ glsafe(::glLineWidth(2.0f));
+ m_cut_contours.contours.render();
+#if !ENABLE_GL_SHADERS_ATTRIBUTES
+ glsafe(::glPopMatrix());
+#endif // !ENABLE_GL_SHADERS_ATTRIBUTES
+#if ENABLE_LEGACY_OPENGL_REMOVAL
+ shader->stop_using();
+ }
+#endif // ENABLE_LEGACY_OPENGL_REMOVAL
+ }
+
+void GLGizmoCut::on_render_for_picking()
+{
+ glsafe(::glDisable(GL_DEPTH_TEST));
+ render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
+}
+
+void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
+{
+ static float last_y = 0.0f;
+ static float last_h = 0.0f;
+
+ m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
+
+ const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
+
+ // adjust window position to avoid overlap the view toolbar
+ const float win_h = ImGui::GetWindowHeight();
+ y = std::min(y, bottom_limit - win_h);
+ ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
+ if (last_h != win_h || last_y != y) {
+ // ask canvas for another frame to render the window in the correct position
+ m_imgui->set_requires_extra_frame();
+ if (last_h != win_h)
+ last_h = win_h;
+ if (last_y != y)
+ last_y = y;
+ }
+
+ ImGui::AlignTextToFramePadding();
+ m_imgui->text("Z");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f);
+
+ double cut_z = m_cut_z;
+ if (imperial_units)
+ cut_z *= ObjectManipulation::mm_to_in;
+ ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
+
+ ImGui::SameLine();
+ m_imgui->text(imperial_units ? _L("in") : _L("mm"));
+
+ m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0);
+
+ ImGui::Separator();
+
+ m_imgui->checkbox(_L("Keep upper part"), m_keep_upper);
+ m_imgui->checkbox(_L("Keep lower part"), m_keep_lower);
+ m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower);
+
+ ImGui::Separator();
+
+ m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || m_cut_z <= 0.0 || m_max_z <= m_cut_z);
+ const bool cut_clicked = m_imgui->button(_L("Perform cut"));
+ m_imgui->disabled_end();
+
+ m_imgui->end();
+
+ if (cut_clicked && (m_keep_upper || m_keep_lower))
+ perform_cut(m_parent.get_selection());
+}
+
+void GLGizmoCut::set_cut_z(double cut_z)
+{
+ // Clamp the plane to the object's bounding box
+ m_cut_z = std::clamp(cut_z, 0.0, m_max_z);
+}
+
+void GLGizmoCut::perform_cut(const Selection& selection)
+{
+ const int instance_idx = selection.get_instance_idx();
+ const int object_idx = selection.get_object_idx();
+
+ wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
+
+ // m_cut_z is the distance from the bed. Subtract possible SLA elevation.
+ const GLVolume* first_glvolume = selection.get_first_volume();
+ const double object_cut_z = m_cut_z - first_glvolume->get_sla_shift_z();
+
+ if (0.0 < object_cut_z && object_cut_z < m_max_z)
+ wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z,
+ only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) |
+ only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) |
+ only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower));
+ else {
+ // the object is SLA-elevated and the plane is under it.
+ }
+}
+
+double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const
+{
+ double projection = 0.0;
+
+ const Vec3d starting_vec = m_drag_pos - m_drag_center;
+ const double len_starting_vec = starting_vec.norm();
+ if (len_starting_vec != 0.0) {
+ const Vec3d mouse_dir = mouse_ray.unit_vector();
+ // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
+ // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
+ // in our case plane normal and ray direction are the same (orthogonal view)
+ // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
+ const Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
+ // vector from the starting position to the found intersection
+ const Vec3d inters_vec = inters - m_drag_pos;
+
+ // finds projection of the vector along the staring direction
+ projection = inters_vec.dot(starting_vec.normalized());
+ }
+ return projection;
+}
+
+BoundingBoxf3 GLGizmoCut::bounding_box() const
+{
+ BoundingBoxf3 ret;
+ const Selection& selection = m_parent.get_selection();
+ const Selection::IndicesList& idxs = selection.get_volume_idxs();
+ return selection.get_bounding_box();
+
+ for (unsigned int i : idxs) {
+ const GLVolume* volume = selection.get_volume(i);
+ if (!volume->is_modifier)
+ ret.merge(volume->transformed_convex_hull_bounding_box());
+ }
+ return ret;
+}
+
+void GLGizmoCut::update_contours()
+{
+ const Selection& selection = m_parent.get_selection();
+ const GLVolume* first_glvolume = selection.get_first_volume();
+ const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box();
+
+ const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
+ const int instance_idx = selection.get_instance_idx();
+ std::vector<ObjectID> volumes_idxs = std::vector<ObjectID>(model_object->volumes.size());
+ for (size_t i = 0; i < model_object->volumes.size(); ++i) {
+ volumes_idxs[i] = model_object->volumes[i]->id();
+ }
+
+ if (0.0 < m_cut_z && m_cut_z < m_max_z) {
+ if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() ||
+ m_cut_contours.instance_idx != instance_idx || m_cut_contours.volumes_idxs != volumes_idxs) {
+ m_cut_contours.cut_z = m_cut_z;
+
+ if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs)
+ m_cut_contours.mesh = model_object->raw_mesh();
+
+ m_cut_contours.position = box.center();
+ m_cut_contours.shift = Vec3d::Zero();
+ m_cut_contours.object_id = model_object->id();
+ m_cut_contours.instance_idx = instance_idx;
+ m_cut_contours.volumes_idxs = volumes_idxs;
+ m_cut_contours.contours.reset();
+
+ MeshSlicingParams slicing_params;
+ slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix();
+ slicing_params.trafo.pretranslate(Vec3d(0., 0., first_glvolume->get_sla_shift_z()));
+
+ const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params);
+ if (!polys.empty()) {
+ m_cut_contours.contours.init_from(polys, static_cast<float>(m_cut_z));
+#if ENABLE_LEGACY_OPENGL_REMOVAL
+ m_cut_contours.contours.set_color(ColorRGBA::WHITE());
+#else
+ m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
+#endif // ENABLE_LEGACY_OPENGL_REMOVAL
+ }
+ }
+ else if (box.center() != m_cut_contours.position) {
+ m_cut_contours.shift = box.center() - m_cut_contours.position;
+ }
+ }
+ else
+ m_cut_contours.contours.reset();
+}
+
+} // namespace GUI
+} // namespace Slic3r