diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3688b10f96..847a15a9da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -21,277 +21,115 @@ namespace Slic3r { namespace GUI { -const double GLGizmoCut::Offset = 10.0; -const double GLGizmoCut::Margin = 20.0; -static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); +const double GLGizmoCenterMove::Margin = 20.0; -GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoCenterMove::GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoMove3D(parent, "", -1) { } -std::string GLGizmoCut::get_tooltip() const +void GLGizmoCenterMove::set_center_pos(const Vec3d& centre_pos) { - 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) : ""; + // Clamp the center position of the cut plane to the object's bounding box + set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()), + std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()), + std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z()))); } -bool GLGizmoCut::on_init() +std::string GLGizmoCenterMove::get_tooltip() const { - m_grabbers.emplace_back(); -// m_shortcut_key = WXK_CONTROL_C; - return true; + double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; + + const Vec3d& center_pos = get_center(); + + if (m_hover_id == 0 || m_grabbers[0].dragging) + return "X: " + format(center_pos.x() * koef, 2); + else if (m_hover_id == 1 || m_grabbers[1].dragging) + return "Y: " + format(center_pos.y() * koef, 2); + else if (m_hover_id == 2 || m_grabbers[2].dragging) + return "Z: " + format(center_pos.z() * koef, 2); + else + return ""; } -std::string GLGizmoCut::on_get_name() const +void GLGizmoCenterMove::on_set_state() { - return _u8L("Cut"); + // Reset internal variables on gizmo activation, if bounding box was changed + if (get_state() == On) { + const BoundingBoxf3 box = bounding_box(); + if (m_max_pos != box.max && m_min_pos != box.min) { + m_max_pos = box.max; + m_min_pos = box.min; + set_center_pos(box.center()); + } + } } -void GLGizmoCut::on_set_state() +void GLGizmoCenterMove::on_update(const UpdateData& data) { - // Reset m_cut_z on gizmo activation - if (get_state() == On && m_cut_z == 0.0) - m_cut_z = bounding_box().center().z(); + GLGizmoMove3D::on_update(data); + set_center_pos(get_center()); } -bool GLGizmoCut::on_is_activable() const +BoundingBoxf3 GLGizmoCenterMove::bounding_box() const { + BoundingBoxf3 ret; const Selection& selection = m_parent.get_selection(); - return selection.is_single_full_instance() && !selection.is_wipe_tower(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + 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::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; + +GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) + , m_move_gizmo(GLGizmoCenterMove(parent, "", -1)) +{ + m_move_gizmo.set_group_id(3); + + m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") +// , _u8L("Radial"), _u8L("Modular") + }; + + m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; + m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; + + m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") +// , _u8L("Claw") + }; + + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") +// , _u8L("D-shape") + }; + + m_axis_names = { "X", "Y", "Z" }; } -void GLGizmoCut::on_update(const UpdateData& data) +std::string GLGizmoCut3D::get_tooltip() const { - if (m_hover_id != -1) - set_cut_z(m_start_z + calc_projection(data.mouse_ray)); + std::string tooltip = m_rotation_gizmo.get_tooltip(); + if (tooltip.empty()) + tooltip = m_move_gizmo.get_tooltip(); + return tooltip; } -void GLGizmoCut::on_render() +void GLGizmoCut3D::shift_cut_z(double delta) { - 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 - plane_center.x(); - //const float max_x = box.max.x() + Margin - plane_center.x(); - //const float min_y = box.min.y() - Margin - plane_center.y(); - //const float max_y = box.max.y() + Margin - plane_center.y(); -// 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_GLBEGIN_GLEND_REMOVAL -//#endif // ENABLE_GLBEGIN_GLEND_REMOVAL -// -// glsafe(::glEnable(GL_CULL_FACE)); -// glsafe(::glDisable(GL_BLEND)); - -#if ENABLE_GLBEGIN_GLEND_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); - - const bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON; - m_old_z = plane_center.z(); - - if (!m_plane.is_initialized() || z_changed) { - m_plane.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - 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_ushort_triangle(0, 1, 2); - init_data.add_ushort_triangle(2, 3, 0); - - m_plane.init_from(std::move(init_data)); - } - - 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_GLBEGIN_GLEND_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_GLBEGIN_GLEND_REMOVAL - if (!m_grabber_connection.is_initialized() || z_changed) { - m_grabber_connection.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.color = ColorRGBA::YELLOW(); - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex((Vec3f)plane_center.cast()); - init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); - - // indices - init_data.add_ushort_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_GLBEGIN_GLEND_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_GLBEGIN_GLEND_REMOVAL - shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - } - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); -// glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(m_angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(m_angles.x()), 1.0, 0.0, 0.0)); - - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); + Vec3d new_cut_center = m_move_gizmo.get_center(); + new_cut_center[Z] += delta; + set_center(new_cut_center); } -void GLGizmoCut::on_render_for_picking() +void GLGizmoCut3D::set_center(const Vec3d& center) { - 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()); + m_move_gizmo.set_center_pos(center); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); } void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) @@ -357,17 +195,19 @@ void GLGizmoCut3D::render_move_center_input(int axis) ImGui::SameLine(); ImGui::PushItemWidth(0.3*m_control_width); - double value = axis == Z ? m_cut_plane_gizmo.get_cut_z() : 0.0; + Vec3d move = m_move_gizmo.get_center(); + double in_val, value = in_val = move[axis]; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - if (axis == Z) { - double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); - m_cut_plane_gizmo.set_cut_z(val); - set_center_z(val); - } + double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + + if (in_val != val) { + move[axis] = val; + set_center(move); + } } void GLGizmoCut3D::render_rotation_input(int axis) @@ -375,8 +215,8 @@ void GLGizmoCut3D::render_rotation_input(int axis) m_imgui->text(m_axis_names[axis] + ":"); ImGui::SameLine(); - Vec3d rotation = get_rotation(); - double value = rotation[axis]; + Vec3d rotation = m_rotation_gizmo.get_rotation(); + double value = rotation[axis] * (180. / M_PI); if (value > 360) value -= 360; @@ -384,9 +224,8 @@ void GLGizmoCut3D::render_rotation_input(int axis) ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - rotation[axis] = value; - set_rotation(rotation); - m_cut_plane_gizmo.set_angles(rotation); + rotation[axis] = (M_PI / 180.) * value; + m_rotation_gizmo.set_rotation(rotation); } void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type) @@ -407,14 +246,13 @@ void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) void GLGizmoCut3D::render_cut_plane() { - const BoundingBoxf3 box = m_cut_plane_gizmo.bounding_box(); - Vec3d plane_center = box.center(); - plane_center.z() = m_cut_plane_gizmo.get_cut_z(); + const BoundingBoxf3 box = m_move_gizmo.bounding_box(); + Vec3d plane_center = m_move_gizmo.get_center();// == Vec3d::Zero() ? box.center() : m_move_gizmo.get_center(); - const float min_x = box.min.x() - GLGizmoCut::Margin - plane_center.x(); - const float max_x = box.max.x() + GLGizmoCut::Margin - plane_center.x(); - const float min_y = box.min.y() - GLGizmoCut::Margin - plane_center.y(); - const float max_y = box.max.y() + GLGizmoCut::Margin - plane_center.y(); + const float min_x = box.min.x() - GLGizmoCenterMove::Margin - plane_center.x(); + const float max_x = box.max.x() + GLGizmoCenterMove::Margin - plane_center.x(); + const float min_y = box.min.y() - GLGizmoCenterMove::Margin - plane_center.y(); + const float max_y = box.max.y() + GLGizmoCenterMove::Margin - plane_center.y(); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); @@ -426,10 +264,7 @@ void GLGizmoCut3D::render_cut_plane() return; shader->start_using(); -// bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON; -// m_old_z = plane_center.z(); - - Vec3d angles = get_rotation(); + Vec3d angles = m_rotation_gizmo.get_rotation(); glsafe(::glPushMatrix()); glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); @@ -437,34 +272,26 @@ void GLGizmoCut3D::render_cut_plane() glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - if (!m_plane.is_initialized()/* || z_changed*/) { + if (!m_plane.is_initialized()) { m_plane.reset(); - GLModel::InitializationData init_data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; - entity.positions.reserve(4); - entity.positions.emplace_back(Vec3f(min_x, min_y, 0.0)); - entity.positions.emplace_back(Vec3f(max_x, min_y, 0.0)); - entity.positions.emplace_back(Vec3f(max_x, max_y, 0.0)); - entity.positions.emplace_back(Vec3f(min_x, max_y, 0.0)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; + init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); - entity.normals.reserve(4); - for (size_t i = 0; i < 4; ++i) { - entity.normals.emplace_back(Vec3f::UnitZ()); - } + // vertices + init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); + init_data.add_vertex(Vec3f(max_x, min_y, 0.0)); + init_data.add_vertex(Vec3f(max_x, max_y, 0.0)); + init_data.add_vertex(Vec3f(min_x, max_y, 0.0)); - entity.indices.reserve(6); - entity.indices.emplace_back(0); - entity.indices.emplace_back(1); - entity.indices.emplace_back(2); - entity.indices.emplace_back(2); - entity.indices.emplace_back(3); - entity.indices.emplace_back(0); + // indices + init_data.add_ushort_triangle(0, 1, 2); + init_data.add_ushort_triangle(2, 3, 0); - init_data.entities.emplace_back(entity); - m_plane.init_from(init_data); - m_plane.set_color(-1, PLANE_COLOR); + m_plane.init_from(std::move(init_data)); } m_plane.render(); @@ -486,159 +313,11 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -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(); - 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 volumes_idxs = std::vector(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 = Vec3d::Zero();//box.center(); - m_cut_contours.shift = box.center();//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(); - - // addition back transformation - Geometry::Transformation m_transformation = Geometry::Transformation(); - m_transformation.set_offset(-first_glvolume->get_instance_transformation().get_offset()); - m_transformation.set_rotation(-m_angles); - auto cut_params = m_transformation.get_matrix(); - - - MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix() * cut_params; - - auto cut_z = m_cut_z - box.center().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(/*m_*/cut_z)); -#if ENABLE_GLBEGIN_GLEND_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_GLBEGIN_GLEND_REMOVAL - } - } - else if (box.center() != m_cut_contours.shift) { - m_cut_contours.shift = box.center();// -m_cut_contours.position; - } - //else if (box.center() != m_cut_contours.position) { - // m_cut_contours.shift = -box.center();// -m_cut_contours.position; - //} - } - else - m_cut_contours.contours.reset(); -} - -GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoRotate3D(parent, icon_filename, sprite_id) - , m_cut_plane_gizmo(GLGizmoCut(parent, "", -1)) -{ - m_cut_plane_gizmo.set_group_id(3); - - m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") -// , _u8L("Radial"), _u8L("Modular") - }; - - m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; - m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; - - m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") -// , _u8L("Claw") - }; - - m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") -// , _u8L("D-shape") - }; - - m_axis_names = {"X", "Y", "Z"}; -} - bool GLGizmoCut3D::on_init() { - if(!GLGizmoRotate3D::on_init()) - return false; - - if (!m_cut_plane_gizmo.init()) + if(!m_rotation_gizmo.init()) + return false; + if(!m_move_gizmo.init()) return false; m_shortcut_key = WXK_CONTROL_C; @@ -650,33 +329,66 @@ std::string GLGizmoCut3D::on_get_name() const return _u8L("Cut"); } +void GLGizmoCut3D::on_set_state() +{ + m_move_gizmo.set_state(m_state); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + m_rotation_gizmo.set_state(m_state); +} + +void GLGizmoCut3D::on_set_hover_id() +{ + int move_group_id = m_move_gizmo.get_group_id(); + m_rotation_gizmo. set_hover_id((m_hover_id < move_group_id) ? m_hover_id : -1); + m_move_gizmo. set_hover_id((m_hover_id >= move_group_id) ? m_hover_id - move_group_id : -1); +} + +void GLGizmoCut3D::on_enable_grabber(unsigned int id) +{ + m_rotation_gizmo.enable_grabber(id); + m_move_gizmo.enable_grabber(id- m_move_gizmo.get_group_id()); +} + +void GLGizmoCut3D::on_disable_grabber(unsigned int id) +{ + m_rotation_gizmo.disable_grabber(id); + m_move_gizmo.disable_grabber(id- m_move_gizmo.get_group_id()); +} + bool GLGizmoCut3D::on_is_activable() const { - return m_cut_plane_gizmo.is_activable(); + return m_move_gizmo.is_activable(); } void GLGizmoCut3D::on_start_dragging() { - GLGizmoRotate3D::on_start_dragging(); - if (m_hover_id == 3) - m_cut_plane_gizmo.start_dragging(); + m_rotation_gizmo.start_dragging(); + m_move_gizmo.start_dragging(); } void GLGizmoCut3D::on_stop_dragging() { - GLGizmoRotate3D::on_stop_dragging(); - if (m_hover_id == 3) - m_cut_plane_gizmo.stop_dragging(); + m_rotation_gizmo.stop_dragging(); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + m_move_gizmo.stop_dragging(); +} + +void GLGizmoCut3D::on_update(const UpdateData& data) +{ + m_move_gizmo.update(data); + m_rotation_gizmo.update(data); } void GLGizmoCut3D::on_render() { render_cut_plane(); if (m_mode == CutMode::cutPlanar) { - GLGizmoRotate3D::on_render(); -// if (m_hover_id == -1 || m_hover_id == 3) + int move_group_id = m_move_gizmo.get_group_id(); + if (m_hover_id < move_group_id) + m_rotation_gizmo.render(); + if (m_hover_id == -1 || m_hover_id >= move_group_id) + m_move_gizmo.render(); } - m_cut_plane_gizmo.render(); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -773,7 +485,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) /*|| m_cut_z <= 0.0 || m_max_z <= m_cut_z*/); + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); @@ -783,6 +495,11 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) perform_cut(m_parent.get_selection()); } +bool GLGizmoCut3D::can_perform_cut() const +{ + return true; +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { const int instance_idx = selection.get_instance_idx(); @@ -792,9 +509,9 @@ void GLGizmoCut3D::perform_cut(const Selection& 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_plane_gizmo.get_cut_z() - first_glvolume->get_sla_shift_z(); + const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z(); - if (0.0 < object_cut_z/* && object_cut_z < m_max_z*/) + if (0.0 < object_cut_z && can_perform_cut()) wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f8d7570a43..d1461404b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" +#include "GLGizmoMove.hpp" #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/ObjectID.hpp" @@ -10,75 +11,33 @@ namespace Slic3r { namespace GUI { -class GLGizmoCut : public GLGizmoBase +class GLGizmoCenterMove : public GLGizmoMove3D { public: - static const double Offset; static const double Margin; private: - double m_cut_z{ 0.0 }; - double m_max_z{ 0.0 }; - double m_start_z{ 0.0 }; - Vec3d m_drag_pos; - Vec3d m_drag_center; - bool m_keep_upper{ true }; - bool m_keep_lower{ true }; - bool m_rotate_lower{ false }; -#if ENABLE_GLBEGIN_GLEND_REMOVAL - GLModel m_grabber_connection; - float m_old_z{ 0.0f }; -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - Vec3d m_angles{ Vec3d::Zero() }; - - struct CutContours - { - TriangleMesh mesh; - GLModel contours; - double cut_z{ 0.0 }; - Vec3d position{ Vec3d::Zero() }; - Vec3d shift{ Vec3d::Zero() }; - ObjectID object_id; - int instance_idx{ -1 }; - std::vector volumes_idxs; - }; - - CutContours m_cut_contours; + Vec3d m_min_pos{ Vec3d::Zero() }; + Vec3d m_max_pos{ Vec3d::Zero() }; public: - GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - - double get_cut_z() const { return m_cut_z; } - void set_cut_z(double cut_z); - void set_angles(const Vec3d& angles) { m_angles = angles; } - + GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; protected: - virtual bool on_init() override; - virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual std::string on_get_name() const override; virtual void on_set_state() override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; virtual void on_update(const UpdateData& data) override; - virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_render_input_window(float x, float y, float bottom_limit) override; - -private: - void perform_cut(const Selection& selection); - double calc_projection(const Linef3& mouse_ray) const; public: - BoundingBoxf3 bounding_box() const; - void update_contours(); + void set_center_pos(const Vec3d& center_pos); + BoundingBoxf3 bounding_box() const; }; -class GLGizmoCut3D : public GLGizmoRotate3D + +class GLGizmoCut3D : public GLGizmoBase { - GLGizmoCut m_cut_plane_gizmo; + GLGizmoRotate3D m_rotation_gizmo; + GLGizmoCenterMove m_move_gizmo; #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel m_plane; @@ -148,57 +107,37 @@ class GLGizmoCut3D : public GLGizmoRotate3D public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - std::string get_tooltip() const override { - std::string tooltip = GLGizmoRotate3D::get_tooltip(); - if (tooltip.empty()) - tooltip = m_cut_plane_gizmo.get_tooltip(); - return tooltip; - } + std::string get_tooltip() const override; + + void shift_cut_z(double delta); protected: bool on_init() override; std::string on_get_name() const override; - void on_set_state() override { - GLGizmoRotate3D::on_set_state(); - m_cut_plane_gizmo.set_state(m_state); - } - void on_set_hover_id() override { - GLGizmoRotate3D::on_set_hover_id(); - m_cut_plane_gizmo.set_hover_id((m_hover_id == 3) ? 0 : -1); - } - void on_enable_grabber(unsigned int id) override { - GLGizmoRotate3D::on_enable_grabber(id); - if (id == 3) - m_cut_plane_gizmo.enable_grabber(0); - } - void on_disable_grabber(unsigned int id) override { - GLGizmoRotate3D::on_disable_grabber(id); - if (id == 3) - m_cut_plane_gizmo.disable_grabber(0); - } + void on_set_state() override; + void on_set_hover_id() override; + void on_enable_grabber(unsigned int id) override; + void on_disable_grabber(unsigned int id) override; bool on_is_activable() const override; void on_start_dragging() override; void on_stop_dragging() override; - void on_update(const UpdateData& data) override { - GLGizmoRotate3D::on_update(data); - m_cut_plane_gizmo.update(data); - } + void on_update(const UpdateData& data) override; void on_render() override; void on_render_for_picking() override { - GLGizmoRotate3D::on_render_for_picking(); - m_cut_plane_gizmo.render_for_picking(); + m_rotation_gizmo.render_for_picking(); + m_move_gizmo.render_for_picking(); } - void on_render_input_window(float x, float y, float bottom_limit) override; private: - + void set_center(const Vec3d& center); void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); void render_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); void render_connect_type_radio_button(ConnectorType type); + bool can_perform_cut() const; void render_cut_plane(); void perform_cut(const Selection& selection); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index ff921ea7ce..e12974b750 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -78,6 +78,9 @@ void GLGizmoMove3D::on_update(const UpdateData& data) m_displacement.y() = calc_projection(data); else if (m_hover_id == 2) m_displacement.z() = calc_projection(data); + + if (m_has_forced_center) + m_center += m_displacement; } void GLGizmoMove3D::on_render() @@ -91,19 +94,27 @@ void GLGizmoMove3D::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const BoundingBoxf3& box = selection.get_bounding_box(); - const Vec3d& center = box.center(); + const Vec3d& center = m_has_forced_center ? m_center : box.center(); - // x axis - m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; - m_grabbers[0].color = AXES_COLOR[0]; + if (m_has_forced_center) + for (auto axis : { X, Y, Z }) { + m_grabbers[axis].center = center; + m_grabbers[axis].center[axis] += 0.5*fabs(box.max[axis] - box.min[axis]); + m_grabbers[axis].color = AXES_COLOR[axis]; + } + else { + // x axis + m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; + m_grabbers[0].color = AXES_COLOR[0]; - // y axis - m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; - m_grabbers[1].color = AXES_COLOR[1]; + // y axis + m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; + m_grabbers[1].color = AXES_COLOR[1]; - // z axis - m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; - m_grabbers[2].color = AXES_COLOR[2]; + // z axis + m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; + m_grabbers[2].color = AXES_COLOR[2]; + } glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -216,7 +227,10 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + const Vec3d& starting_drag_position = m_has_forced_center ? m_grabbers[m_hover_id].center : m_starting_drag_position; + const Vec3d& starting_box_center = m_has_forced_center ? m_center : m_starting_box_center; + + Vec3d starting_vec = starting_drag_position - starting_box_center; double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); @@ -224,9 +238,9 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const // 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 - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; + Vec3d inters_vec = inters - starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 2a75df8669..9e404a95cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -17,6 +17,9 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_starting_box_center{ Vec3d::Zero() }; Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; + Vec3d m_center{ Vec3d::Zero() }; + bool m_has_forced_center{ false }; + GLModel m_cone; #if ENABLE_GLBEGIN_GLEND_REMOVAL struct GrabberConnection @@ -38,6 +41,9 @@ public: std::string get_tooltip() const override; + void set_center(Vec3d center) { m_center = center; m_has_forced_center = true; } + const Vec3d& get_center() const { return m_center; } + protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 9869634cc2..b61310d6cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -38,9 +38,10 @@ void GLGizmoRotate::set_angle(double angle) m_angle = angle; } -void GLGizmoRotate::set_center_z(double center_z) +void GLGizmoRotate::set_center(const Vec3d& center) { - m_center_z = center_z; + m_forced_center = center; + m_has_forced_center = true; } std::string GLGizmoRotate::get_tooltip() const @@ -64,9 +65,7 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_start_dragging() { const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_center = box.center(); - if (m_center_z >= 0) - m_center[Z] = m_center_z; + m_center = m_has_forced_center ? m_forced_center : box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; @@ -118,9 +117,7 @@ void GLGizmoRotate::on_render() const BoundingBoxf3& box = selection.get_bounding_box(); if (m_hover_id != 0 && !m_grabbers.front().dragging) { - m_center = box.center(); - if (m_center_z >= 0) - m_center[Z] = m_center_z; + m_center = m_has_forced_center ? m_forced_center : box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 0c291cbef1..7eee2069c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -28,13 +28,14 @@ public: private: Axis m_axis; double m_angle{ 0.0 }; - double m_center_z{ -1.0 }; Vec3d m_center{ Vec3d::Zero() }; float m_radius{ 0.0f }; float m_snap_coarse_in_radius{ 0.0f }; float m_snap_coarse_out_radius{ 0.0f }; float m_snap_fine_in_radius{ 0.0f }; float m_snap_fine_out_radius{ 0.0f }; + bool m_has_forced_center{false}; + Vec3d m_forced_center{ Vec3d::Zero() }; GLModel m_cone; #if ENABLE_GLBEGIN_GLEND_REMOVAL @@ -60,6 +61,7 @@ public: double get_angle() const { return m_angle; } void set_angle(double angle); void set_center_z(double center_z); + void set_center(const Vec3d& center); std::string get_tooltip() const override; @@ -103,7 +105,7 @@ public: Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } - void set_center_z(double center_z) { m_gizmos[X].set_center_z(center_z); m_gizmos[Y].set_center_z(center_z); m_gizmos[Z].set_center_z(center_z); } + void set_center(const Vec3d& center) { m_gizmos[X].set_center(center); m_gizmos[Y].set_center(center); m_gizmos[Z].set_center(center); } std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 3a144a65d1..ba1419fea4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -919,8 +919,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) else if (m_current == Cut) { auto do_move = [this, &processed](double delta_z) { - GLGizmoCut* cut = dynamic_cast(get_current()); - cut->set_cut_z(delta_z + cut->get_cut_z()); + GLGizmoCut3D* cut = dynamic_cast(get_current()); + cut->shift_cut_z(delta_z); processed = true; };