From 116f928903725485d0d5c690c4906fb083807dea Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 8 Oct 2021 14:32:02 +0200 Subject: [PATCH 01/50] Tech ENABLE_WORLD_COORDINATE - 1st installment 1) Added combo to select world/local coordinate to part manipulator in sidebar 2) Gizmo move oriented in dependence of the selected coordinate system 3) Sidebar hints for position oriented in dependence of the selected coordinate system --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 61 +++++++++++-- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 103 ++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 18 ++-- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 + src/slic3r/GUI/Selection.cpp | 59 +++++++++++-- 8 files changed, 211 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index acbb4731d3..d8cdb515b3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -80,6 +80,8 @@ // Enable rendering modifiers and similar objects always as transparent #define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4) +// Enable editing volumes transformation in world coordinates and instances in local coordinates +#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d5a1a5659f..564bba5db7 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -444,8 +444,13 @@ void ObjectManipulation::Show(const bool show) if (show) { // Show the "World Coordinates" / "Local Coordintes" Combo in Advanced / Expert mode only. - bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; - m_word_local_combo->Show(show_world_local_combo); +#if ENABLE_WORLD_COORDINATE + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); +#else + bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; +#endif // ENABLE_WORLD_COORDINATE + m_word_local_combo->Show(show_world_local_combo); m_empty_str->Show(!show_world_local_combo); } } @@ -529,7 +534,9 @@ void ObjectManipulation::update_settings_value(const Selection& selection) if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if !ENABLE_WORLD_COORDINATE m_new_position = volume->get_instance_offset(); +#endif // !ENABLE_WORLD_COORDINATE // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. if (m_world_coordinates && ! m_uniform_scale && @@ -540,14 +547,20 @@ void ObjectManipulation::update_settings_value(const Selection& selection) } if (m_world_coordinates) { - m_new_rotate_label_string = L("Rotate"); +#if ENABLE_WORLD_COORDINATE + m_new_position = volume->get_instance_offset(); +#endif // ENABLE_WORLD_COORDINATE + m_new_rotate_label_string = L("Rotate"); m_new_rotation = Vec3d::Zero(); m_new_size = selection.get_scaled_instance_bounding_box().size(); - m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.; + m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.0; } else { - m_new_rotation = volume->get_instance_rotation() * (180. / M_PI); - m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); +#if ENABLE_WORLD_COORDINATE + m_new_position = Vec3d::Zero(); +#endif // ENABLE_WORLD_COORDINATE + m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); + m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); m_new_scale = volume->get_instance_scaling_factor() * 100.; } @@ -566,10 +579,29 @@ void ObjectManipulation::update_settings_value(const Selection& selection) else if (selection.is_single_modifier() || selection.is_single_volume()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_WORLD_COORDINATE + if (m_world_coordinates) { + const Geometry::Transformation trafo(volume->world_matrix()); + + const Vec3d& offset = trafo.get_offset(); + const Vec3d& rotation = trafo.get_rotation(); + const Vec3d& scaling_factor = trafo.get_scaling_factor(); + const Vec3d& m = trafo.get_mirror(); + + m_new_position = offset; + m_new_rotation = rotation * (180.0 / M_PI); + m_new_scale = scaling_factor * 100.0; + m_new_size = volume->bounding_box().size().cwiseProduct(scaling_factor); + } + else { +#endif // ENABLE_WORLD_COORDINATE m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation() * (180. / M_PI); m_new_scale = volume->get_volume_scaling_factor() * 100.; - m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); + m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); +#if ENABLE_WORLD_COORDINATE + } +#endif // ENABLE_WORLD_COORDINATE m_new_enabled = true; } else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) { @@ -814,7 +846,11 @@ void ObjectManipulation::change_position_value(int axis, double value) auto canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); selection.start_dragging(); +#if ENABLE_WORLD_COORDINATE + selection.translate(position - m_cache.position, !m_world_coordinates); +#else selection.translate(position - m_cache.position, selection.requires_local_axes()); +#endif // ENABLE_WORLD_COORDINATE canvas->do_move(L("Set Position")); m_cache.position = position; @@ -974,6 +1010,17 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) m_uniform_scale = new_value; } +#if ENABLE_WORLD_COORDINATE +void ObjectManipulation::set_world_coordinates(const bool world_coordinates) +{ + m_world_coordinates = world_coordinates; + this->UpdateAndShow(true); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); +} +#endif // ENABLE_WORLD_COORDINATE + void ObjectManipulation::msw_rescale() { const int em = wxGetApp().em_unit(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index a15c72fb8e..6d0383038e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -183,7 +183,11 @@ public: void set_uniform_scaling(const bool uniform_scale); bool get_uniform_scaling() const { return m_uniform_scale; } // Does the object manipulation panel work in World or Local coordinates? +#if ENABLE_WORLD_COORDINATE + void set_world_coordinates(const bool world_coordinates); +#else void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); } +#endif // ENABLE_WORLD_COORDINATE bool get_world_coordinates() const { return m_world_coordinates; } void reset_cache() { m_cache.reset(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index e73a85647e..13071cb469 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -2,6 +2,9 @@ #include "GLGizmoMove.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" +#if ENABLE_WORLD_COORDINATE +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#endif // ENABLE_WORLD_COORDINATE #include @@ -14,11 +17,6 @@ const double GLGizmoMove3D::Offset = 10.0; GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_displacement(Vec3d::Zero()) - , m_snap_step(1.0) - , m_starting_drag_position(Vec3d::Zero()) - , m_starting_box_center(Vec3d::Zero()) - , m_starting_box_bottom_center(Vec3d::Zero()) { m_vbo_cone.init_from(its_make_cone(1., 1., 2*PI/36)); } @@ -26,15 +24,15 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam std::string GLGizmoMove3D::get_tooltip() const { const Selection& selection = m_parent.get_selection(); - bool show_position = selection.is_single_full_instance(); + const bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); if (m_hover_id == 0 || m_grabbers[0].dragging) - return "X: " + format(show_position ? position(0) : m_displacement(0), 2); + return "X: " + format(show_position ? position.x() : m_displacement.x(), 2); else if (m_hover_id == 1 || m_grabbers[1].dragging) - return "Y: " + format(show_position ? position(1) : m_displacement(1), 2); + return "Y: " + format(show_position ? position.y() : m_displacement.y(), 2); else if (m_hover_id == 2 || m_grabbers[2].dragging) - return "Z: " + format(show_position ? position(2) : m_displacement(2), 2); + return "Z: " + format(show_position ? position.z() : m_displacement.z(), 2); else return ""; } @@ -65,17 +63,27 @@ void GLGizmoMove3D::on_start_dragging() if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); +#if ENABLE_WORLD_COORDINATE + const Vec3d center = box.center(); + m_starting_drag_position = center + m_grabbers[m_hover_id].center; + m_starting_box_center = center; + m_starting_box_bottom_center = center; + m_starting_box_bottom_center.z() = box.min.z(); +#else m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); - m_starting_box_bottom_center(2) = box.min(2); + m_starting_box_bottom_center.z() = box.min.z(); +#endif // ENABLE_WORLD_COORDINATE } } +#if !ENABLE_WORLD_COORDINATE void GLGizmoMove3D::on_stop_dragging() { m_displacement = Vec3d::Zero(); } +#endif // !ENABLE_WORLD_COORDINATE void GLGizmoMove3D::on_update(const UpdateData& data) { @@ -89,12 +97,30 @@ void GLGizmoMove3D::on_update(const UpdateData& data) void GLGizmoMove3D::on_render() { - const Selection& selection = m_parent.get_selection(); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); + const Selection& selection = m_parent.get_selection(); const BoundingBoxf3& box = selection.get_bounding_box(); +#if ENABLE_WORLD_COORDINATE + glsafe(::glPushMatrix()); + transform_to_local(selection); + + const Vec3d zero = Vec3d::Zero(); + const Vec3d half_box_size = 0.5 * box.size(); + + // x axis + m_grabbers[0].center = { half_box_size.x() + Offset, 0.0, 0.0 }; + m_grabbers[0].color = AXES_COLOR[0]; + + // y axis + m_grabbers[1].center = { 0.0, half_box_size.y() + Offset, 0.0 }; + m_grabbers[1].color = AXES_COLOR[1]; + + // z axis + m_grabbers[2].center = { 0.0, 0.0, half_box_size.z() + Offset }; + m_grabbers[2].color = AXES_COLOR[2]; +#else const Vec3d& center = box.center(); // x axis @@ -108,6 +134,7 @@ void GLGizmoMove3D::on_render() // z axis m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; +#endif // ENABLE_WORLD_COORDINATE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -117,7 +144,11 @@ void GLGizmoMove3D::on_render() if (m_grabbers[i].enabled) { glsafe(::glColor4fv(AXES_COLOR[i].data())); ::glBegin(GL_LINES); +#if ENABLE_WORLD_COORDINATE + ::glVertex3dv(zero.data()); +#else ::glVertex3dv(center.data()); +#endif // ENABLE_WORLD_COORDINATE ::glVertex3dv(m_grabbers[i].center.data()); glsafe(::glEnd()); } @@ -134,7 +165,11 @@ void GLGizmoMove3D::on_render() // draw axis glsafe(::glColor4fv(AXES_COLOR[m_hover_id].data())); ::glBegin(GL_LINES); +#if ENABLE_WORLD_COORDINATE + ::glVertex3dv(zero.data()); +#else ::glVertex3dv(center.data()); +#endif // ENABLE_WORLD_COORDINATE ::glVertex3dv(m_grabbers[m_hover_id].center.data()); glsafe(::glEnd()); @@ -143,40 +178,57 @@ void GLGizmoMove3D::on_render() shader->start_using(); shader->set_uniform("emission_factor", 0.1f); // draw grabber - float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); + const Vec3d box_size = box.size(); + const float mean_size = (float)((box_size.x() + box_size.y() + box_size.z()) / 3.0); m_grabbers[m_hover_id].render(true, mean_size); shader->stop_using(); } render_grabber_extension((Axis)m_hover_id, box, false); } + +#if ENABLE_WORLD_COORDINATE + glsafe(::glPopMatrix()); +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoMove3D::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); +#if ENABLE_WORLD_COORDINATE + const Selection& selection = m_parent.get_selection(); + const BoundingBoxf3& box = selection.get_bounding_box(); + glsafe(::glPushMatrix()); + transform_to_local(selection); +#else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); +#endif // ENABLE_WORLD_COORDINATE + render_grabbers_for_picking(box); render_grabber_extension(X, box, true); render_grabber_extension(Y, box, true); render_grabber_extension(Z, box, true); + +#if ENABLE_WORLD_COORDINATE + glsafe(::glPopMatrix()); +#endif // ENABLE_WORLD_COORDINATE } double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; - double len_starting_vec = starting_vec.norm(); + const Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.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 - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_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; + const Vec3d inters_vec = inters - m_starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); @@ -190,8 +242,9 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const { - float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); - double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size); + const Vec3d box_size = box.size(); + const float mean_size = (float)((box_size.x() + box_size.y() + box_size.z()) / 3.0); + const double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size); std::array color = m_grabbers[axis].color; if (!picking && m_hover_id != -1) { @@ -227,6 +280,18 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box shader->stop_using(); } +#if ENABLE_WORLD_COORDINATE +void GLGizmoMove3D::transform_to_local(const Selection& selection) const +{ + const Vec3d center = selection.get_bounding_box().center(); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + + if (!wxGetApp().obj_manipul()->get_world_coordinates()) { + const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } +} +#endif // ENABLE_WORLD_COORDINATE } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index baa2df7391..67d9390f10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -11,14 +11,11 @@ class GLGizmoMove3D : public GLGizmoBase { static const double Offset; - Vec3d m_displacement; - - double m_snap_step; - - Vec3d m_starting_drag_position; - Vec3d m_starting_box_center; - Vec3d m_starting_box_bottom_center; - + Vec3d m_displacement{ Vec3d::Zero() }; + double m_snap_step{ 1.0 }; + Vec3d m_starting_drag_position{ Vec3d::Zero() }; + Vec3d m_starting_box_center{ Vec3d::Zero() }; + Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; GLModel m_vbo_cone; public: @@ -36,7 +33,9 @@ protected: virtual bool on_init() override; virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; +#if !ENABLE_WORLD_COORDINATE virtual void on_start_dragging() override; +#endif // !ENABLE_WORLD_COORDINATE virtual void on_stop_dragging() override; virtual void on_update(const UpdateData& data) override; virtual void on_render() override; @@ -45,6 +44,9 @@ protected: private: double calc_projection(const UpdateData& data) const; void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const; +#if ENABLE_WORLD_COORDINATE + void transform_to_local(const Selection& selection) const; +#endif // ENABLE_WORLD_COORDINATE }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 54fcfc2c7c..37ca01b3aa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -365,7 +365,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick void GLGizmoRotate::transform_to_local(const Selection& selection) const { - glsafe(::glTranslated(m_center(0), m_center(1), m_center(2))); + glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 08a94a97d7..fa90809b91 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -617,7 +617,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Move: { // Apply new temporary offset +#if ENABLE_WORLD_COORDINATE + selection.translate(get_displacement(), !wxGetApp().obj_manipul()->get_world_coordinates()); +#else selection.translate(get_displacement()); +#endif // ENABLE_WORLD_COORDINATE wxGetApp().obj_manipul()->set_dirty(); break; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 6a82ca8d3e..cace704914 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -706,13 +706,31 @@ void Selection::translate(const Vec3d& displacement, bool local) if (local) v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); else { +#if ENABLE_WORLD_COORDINATE + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d local_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix() * volume_data.get_instance_mirror_matrix()).inverse() * displacement; + v.set_volume_offset(volume_data.get_volume_position() + local_displacement); +#else const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); +#endif // ENABLE_WORLD_COORDINATE } } else if (m_mode == Instance) { +#if ENABLE_WORLD_COORDINATE + if (is_from_fully_selected_instance(i)) { + if (local) { + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; + v.set_instance_offset(volume_data.get_instance_position() + world_displacement); + } + else + v.set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + } +#else if (is_from_fully_selected_instance(i)) v.set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); +#endif // ENABLE_WORLD_COORDINATE else { const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); @@ -1249,10 +1267,28 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const glsafe(::glPushMatrix()); if (!boost::starts_with(sidebar_field, "layer")) { - const Vec3d& center = get_bounding_box().center(); + const Vec3d center = get_bounding_box().center(); if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) { - glsafe(::glTranslated(center(0), center(1), center(2))); + glsafe(::glTranslated(center.x(), center.y(), center.z())); +#if ENABLE_WORLD_COORDINATE + Transform3d orient_matrix = Transform3d::Identity(); + if (boost::starts_with(sidebar_field, "position") || boost::starts_with(sidebar_field, "scale")) + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + else if (boost::starts_with(sidebar_field, "rotation")) { + if (boost::ends_with(sidebar_field, "x")) + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + else if (boost::ends_with(sidebar_field, "y")) { + const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation(); + if (rotation.x() == 0.0) + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + else + orient_matrix.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ())); + } + } + + glsafe(::glMultMatrixd(orient_matrix.data())); +#else if (!boost::starts_with(sidebar_field, "position")) { Transform3d orient_matrix = Transform3d::Identity(); if (boost::starts_with(sidebar_field, "scale")) @@ -1271,15 +1307,24 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const glsafe(::glMultMatrixd(orient_matrix.data())); } - } else if (is_single_volume() || is_single_modifier()) { - glsafe(::glTranslated(center(0), center(1), center(2))); +#endif // ENABLE_WORLD_COORDINATE + } + else if (is_single_volume() || is_single_modifier()) { + glsafe(::glTranslated(center.x(), center.y(), center.z())); +#if ENABLE_WORLD_COORDINATE + if (!wxGetApp().obj_manipul()->get_world_coordinates()) { + const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } +#else Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); if (!boost::starts_with(sidebar_field, "position")) orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); - glsafe(::glMultMatrixd(orient_matrix.data())); - } else { - glsafe(::glTranslated(center(0), center(1), center(2))); +#endif // ENABLE_WORLD_COORDINATE + } + else { + glsafe(::glTranslated(center.x(), center.y(), center.z())); if (requires_local_axes()) { const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); From 2ff2c14f9d66cd299a5cc20efe6dfc12b4fe7b8c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 8 Oct 2021 14:43:46 +0200 Subject: [PATCH 02/50] Fixed syntax error introduced with 116f928903725485d0d5c690c4906fb083807dea --- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 67d9390f10..514c0df2c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -33,10 +33,10 @@ protected: virtual bool on_init() override; virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; -#if !ENABLE_WORLD_COORDINATE virtual void on_start_dragging() override; -#endif // !ENABLE_WORLD_COORDINATE +#if !ENABLE_WORLD_COORDINATE virtual void on_stop_dragging() override; +#endif // !ENABLE_WORLD_COORDINATE virtual void on_update(const UpdateData& data) override; virtual void on_render() override; virtual void on_render_for_picking() override; From 0ed783c6a9df098ae78004c10640873d32ae20f4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 11 Oct 2021 09:52:39 +0200 Subject: [PATCH 03/50] Tech ENABLE_WORLD_COORDINATE - Modified text of tooltips for Gizmo Move --- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 13071cb469..bc40476c43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -25,6 +25,25 @@ std::string GLGizmoMove3D::get_tooltip() const { const Selection& selection = m_parent.get_selection(); const bool show_position = selection.is_single_full_instance(); +#if ENABLE_WORLD_COORDINATE + const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); + Vec3d position = Vec3d::Zero(); + if (!world_coordinates) { + if (selection.is_single_modifier() || selection.is_single_volume() || selection.is_wipe_tower()) + position = selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_offset(); + } + else + position = selection.get_bounding_box().center(); + + if (m_hover_id == 0) + return m_grabbers[0].dragging ? "DX: " + format(m_displacement.x(), 2) : "X: " + format(position.x(), 2); + else if (m_hover_id == 1) + return m_grabbers[1].dragging ? "DY: " + format(m_displacement.y(), 2) : "Y: " + format(position.y(), 2); + else if (m_hover_id == 2) + return m_grabbers[2].dragging ? "DZ: " + format(m_displacement.z(), 2) : "Z: " + format(position.z(), 2); + else + return ""; +#else const Vec3d& position = selection.get_bounding_box().center(); if (m_hover_id == 0 || m_grabbers[0].dragging) @@ -35,6 +54,7 @@ std::string GLGizmoMove3D::get_tooltip() const return "Z: " + format(show_position ? position.z() : m_displacement.z(), 2); else return ""; +#endif // ENABLE_WORLD_COORDINATE } bool GLGizmoMove3D::on_init() @@ -221,7 +241,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const const Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); + const Vec3d mouse_dir = data.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) From 7f6f5dab83db71d165959f48a27c952c258812fb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 Oct 2021 11:07:31 +0200 Subject: [PATCH 04/50] Tech ENABLE_WORLD_COORDINATE - Gizmo rotate oriented in dependence of the selected coordinate system --- src/libslic3r/Geometry.cpp | 10 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 48 +++++-- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 21 +-- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 2 - src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 163 ++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 25 ++-- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 + src/slic3r/GUI/Selection.cpp | 156 +++++++++++---------- src/slic3r/GUI/Selection.hpp | 10 +- 10 files changed, 251 insertions(+), 191 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 420ab473f0..9b80149176 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1529,17 +1529,17 @@ Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot { return // From the current coordinate system to world. - Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) * + Eigen::AngleAxisd(rot_xyz_to.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to.x(), Vec3d::UnitX()) * // From world to the initial coordinate system. - Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ()); + Eigen::AngleAxisd(-rot_xyz_from.x(), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from.z(), Vec3d::UnitZ()); } // This should only be called if it is known, that the two rotations only differ in rotation around the Z axis. double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { - Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); + const Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + const Vec3d& axis = angle_axis.axis(); + const double angle = angle_axis.angle(); #ifndef NDEBUG if (std::abs(angle) > 1e-8) { assert(std::abs(axis.x()) < 1e-8); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 564bba5db7..1c07c5c42c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -278,7 +278,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : return; // Update mirroring at the GLVolumes. - selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL); selection.synchronize_unselected_volumes(); // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. canvas->do_mirror(L("Set Mirror")); @@ -379,7 +379,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : return; // Update rotation at the GLVolumes. - selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL); selection.synchronize_unselected_volumes(); // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. canvas->do_rotate(L("Reset Rotation")); @@ -551,17 +551,23 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_position = volume->get_instance_offset(); #endif // ENABLE_WORLD_COORDINATE m_new_rotate_label_string = L("Rotate"); - m_new_rotation = Vec3d::Zero(); - m_new_size = selection.get_scaled_instance_bounding_box().size(); +#if ENABLE_WORLD_COORDINATE + m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); +#else + m_new_rotation = Vec3d::Zero(); +#endif // ENABLE_WORLD_COORDINATE + m_new_size = selection.get_scaled_instance_bounding_box().size(); m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.0; } else { #if ENABLE_WORLD_COORDINATE m_new_position = Vec3d::Zero(); -#endif // ENABLE_WORLD_COORDINATE + m_new_rotation = Vec3d::Zero(); +#else m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); +#endif // ENABLE_WORLD_COORDINATE m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); - m_new_scale = volume->get_instance_scaling_factor() * 100.; + m_new_scale = volume->get_instance_scaling_factor() * 100.0; } m_new_enabled = true; @@ -570,7 +576,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) const BoundingBoxf3& box = selection.get_bounding_box(); m_new_position = box.center(); m_new_rotation = Vec3d::Zero(); - m_new_scale = Vec3d(100., 100., 100.); + m_new_scale = Vec3d(100.0, 100.0, 100.0); m_new_size = box.size(); m_new_rotate_label_string = L("Rotate"); m_new_scale_label_string = L("Scale"); @@ -586,7 +592,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) const Vec3d& offset = trafo.get_offset(); const Vec3d& rotation = trafo.get_rotation(); const Vec3d& scaling_factor = trafo.get_scaling_factor(); - const Vec3d& m = trafo.get_mirror(); +// const Vec3d& mirror = trafo.get_mirror(); m_new_position = offset; m_new_rotation = rotation * (180.0 / M_PI); @@ -596,8 +602,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection) else { #endif // ENABLE_WORLD_COORDINATE m_new_position = volume->get_volume_offset(); - m_new_rotation = volume->get_volume_rotation() * (180. / M_PI); - m_new_scale = volume->get_volume_scaling_factor() * 100.; + m_new_rotation = volume->get_volume_rotation() * (180.0 / M_PI); + m_new_scale = volume->get_volume_scaling_factor() * 100.0; m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); #if ENABLE_WORLD_COORDINATE } @@ -711,11 +717,20 @@ void ObjectManipulation::update_reset_buttons_visibility() if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_WORLD_COORDINATE + Vec3d rotation = Vec3d::Zero(); + Vec3d scale = Vec3d::Ones(); +#else Vec3d rotation; Vec3d scale; - double min_z = 0.; +#endif // ENABLE_WORLD_COORDINATE + double min_z = 0.0; +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_full_instance() && m_world_coordinates) { +#else if (selection.is_single_full_instance()) { +#endif // ENABLE_WORLD_COORDINATE rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z(); @@ -727,7 +742,11 @@ void ObjectManipulation::update_reset_buttons_visibility() } show_rotation = !rotation.isApprox(Vec3d::Zero()); show_scale = !scale.isApprox(Vec3d::Ones()); +#if ENABLE_WORLD_COORDINATE + show_drop_to_bed = min_z > EPSILON; +#else show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; +#endif // ENABLE_WORLD_COORDINATE } wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { @@ -1019,6 +1038,13 @@ void ObjectManipulation::set_world_coordinates(const bool world_coordinates) canvas->set_as_dirty(); canvas->request_extra_frame(); } + +bool ObjectManipulation::get_world_coordinates() const +{ + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + return wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()) ? + m_world_coordinates : true; +} #endif // ENABLE_WORLD_COORDINATE void ObjectManipulation::msw_rescale() diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 6d0383038e..a4f826feac 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -185,10 +185,11 @@ public: // Does the object manipulation panel work in World or Local coordinates? #if ENABLE_WORLD_COORDINATE void set_world_coordinates(const bool world_coordinates); + bool get_world_coordinates() const; #else void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); } -#endif // ENABLE_WORLD_COORDINATE bool get_world_coordinates() const { return m_world_coordinates; } +#endif // ENABLE_WORLD_COORDINATE void reset_cache() { m_cache.reset(); } #ifndef __APPLE__ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index bc40476c43..a8df103fcc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -23,27 +23,18 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam std::string GLGizmoMove3D::get_tooltip() const { - const Selection& selection = m_parent.get_selection(); - const bool show_position = selection.is_single_full_instance(); #if ENABLE_WORLD_COORDINATE - const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); - Vec3d position = Vec3d::Zero(); - if (!world_coordinates) { - if (selection.is_single_modifier() || selection.is_single_volume() || selection.is_wipe_tower()) - position = selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_offset(); - } - else - position = selection.get_bounding_box().center(); - if (m_hover_id == 0) - return m_grabbers[0].dragging ? "DX: " + format(m_displacement.x(), 2) : "X: " + format(position.x(), 2); + return "X: " + format(m_displacement.x(), 2); else if (m_hover_id == 1) - return m_grabbers[1].dragging ? "DY: " + format(m_displacement.y(), 2) : "Y: " + format(position.y(), 2); + return "Y: " + format(m_displacement.y(), 2); else if (m_hover_id == 2) - return m_grabbers[2].dragging ? "DZ: " + format(m_displacement.z(), 2) : "Z: " + format(position.z(), 2); + return "Z: " + format(m_displacement.z(), 2); else return ""; #else + const Selection& selection = m_parent.get_selection(); + const bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); if (m_hover_id == 0 || m_grabbers[0].dragging) @@ -98,12 +89,10 @@ void GLGizmoMove3D::on_start_dragging() } } -#if !ENABLE_WORLD_COORDINATE void GLGizmoMove3D::on_stop_dragging() { m_displacement = Vec3d::Zero(); } -#endif // !ENABLE_WORLD_COORDINATE void GLGizmoMove3D::on_update(const UpdateData& data) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 514c0df2c1..4a44b3c95e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -34,9 +34,7 @@ protected: virtual std::string on_get_name() const override; virtual bool on_is_activable() const override; virtual void on_start_dragging() override; -#if !ENABLE_WORLD_COORDINATE virtual void on_stop_dragging() override; -#endif // !ENABLE_WORLD_COORDINATE virtual void on_update(const UpdateData& data) override; virtual void on_render() override; virtual void on_render_for_picking() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 37ca01b3aa..5bafc573bd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -2,15 +2,18 @@ #include "GLGizmoRotate.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" - -#include +#if ENABLE_WORLD_COORDINATE +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#endif // ENABLE_WORLD_COORDINATE #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp" + #include "libslic3r/PresetBundle.hpp" -#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp" +#include namespace Slic3r { namespace GUI { @@ -29,13 +32,6 @@ const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) : GLGizmoBase(parent, "", -1) , m_axis(axis) - , m_angle(0.0) - , m_center(0.0, 0.0, 0.0) - , m_radius(0.0f) - , m_snap_coarse_in_radius(0.0f) - , m_snap_coarse_out_radius(0.0f) - , m_snap_fine_in_radius(0.0f) - , m_snap_fine_out_radius(0.0f) { } @@ -81,6 +77,9 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_start_dragging() { +#if ENABLE_WORLD_COORDINATE + init_data_from_selection(m_parent.get_selection()); +#else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_center = box.center(); m_radius = Offset + box.radius(); @@ -88,33 +87,31 @@ void GLGizmoRotate::on_start_dragging() m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; m_snap_fine_in_radius = m_radius; m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoRotate::on_update(const UpdateData& data) { - Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); - Vec2d orig_dir = Vec2d::UnitX(); - Vec2d new_dir = mouse_pos.normalized(); + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); if (cross2(orig_dir, new_dir) < 0.0) theta = 2.0 * (double)PI - theta; - double len = mouse_pos.norm(); + const double len = mouse_pos.norm(); // snap to coarse snap region - if ((m_snap_coarse_in_radius <= len) && (len <= m_snap_coarse_out_radius)) - { - double step = 2.0 * (double)PI / (double)SnapRegionsCount; + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = 2.0 * (double)PI / (double)SnapRegionsCount; theta = step * (double)std::round(theta / step); } - else - { + else { // snap to fine snap region (scale) - if ((m_snap_fine_in_radius <= len) && (len <= m_snap_fine_out_radius)) - { - double step = 2.0 * (double)PI / (double)ScaleStepsCount; + if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = 2.0 * (double)PI / (double)ScaleStepsCount; theta = step * (double)std::round(theta / step); } } @@ -134,12 +131,16 @@ void GLGizmoRotate::on_render() const BoundingBoxf3& box = selection.get_bounding_box(); if (m_hover_id != 0 && !m_grabbers[0].dragging) { +#if ENABLE_WORLD_COORDINATE + init_data_from_selection(selection); +#else m_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; m_snap_fine_in_radius = m_radius; m_snap_fine_out_radius = m_radius * (1.0f + ScaleLongTooth); +#endif // ENABLE_WORLD_COORDINATE } glsafe(::glEnable(GL_DEPTH_TEST)); @@ -186,6 +187,24 @@ void GLGizmoRotate::on_render_for_picking() glsafe(::glPopMatrix()); } +#if ENABLE_WORLD_COORDINATE +void GLGizmoRotate::init_data_from_selection(const Selection& selection) +{ + const BoundingBoxf3& box = selection.get_bounding_box(); + m_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; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; + + if (wxGetApp().obj_manipul()->get_world_coordinates()) + m_orient_matrix = Transform3d::Identity(); + else + m_orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); +} +#endif // ENABLE_WORLD_COORDINATE + void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit) { if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) @@ -219,12 +238,11 @@ void GLGizmoRotate3D::load_rotoptimize_state() void GLGizmoRotate::render_circle() const { ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float x = ::cos(angle) * m_radius; - float y = ::sin(angle) * m_radius; - float z = 0.0f; + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = (float)i * ScaleStepRad; + const float x = ::cos(angle) * m_radius; + const float y = ::sin(angle) * m_radius; + const float z = 0.0f; ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); } glsafe(::glEnd()); @@ -232,21 +250,20 @@ void GLGizmoRotate::render_circle() const void GLGizmoRotate::render_scale() const { - float out_radius_long = m_snap_fine_out_radius; - float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); + const float out_radius_long = m_snap_fine_out_radius; + const float out_radius_short = m_radius * (1.0f + 0.5f * ScaleLongTooth); ::glBegin(GL_LINES); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * m_radius; - float in_y = sina * m_radius; - float in_z = 0.0f; - float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; - float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; - float out_z = 0.0f; + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = (float)i * ScaleStepRad; + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * m_radius; + const float in_y = sina * m_radius; + const float in_z = 0.0f; + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + const float out_z = 0.0f; ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); } @@ -255,23 +272,22 @@ void GLGizmoRotate::render_scale() const void GLGizmoRotate::render_snap_radii() const { - float step = 2.0f * (float)PI / (float)SnapRegionsCount; + const float step = 2.0f * (float)PI / (float)SnapRegionsCount; - float in_radius = m_radius / 3.0f; - float out_radius = 2.0f * in_radius; + const float in_radius = m_radius / 3.0f; + const float out_radius = 2.0f * in_radius; ::glBegin(GL_LINES); - for (unsigned int i = 0; i < SnapRegionsCount; ++i) - { - float angle = (float)i * step; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * in_radius; - float in_y = sina * in_radius; - float in_z = 0.0f; - float out_x = cosa * out_radius; - float out_y = sina * out_radius; - float out_z = 0.0f; + for (unsigned int i = 0; i < SnapRegionsCount; ++i) { + const float angle = (float)i * step; + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float in_z = 0.0f; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + const float out_z = 0.0f; ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); } @@ -288,16 +304,15 @@ void GLGizmoRotate::render_reference_radius() const void GLGizmoRotate::render_angle() const { - float step_angle = (float)m_angle / AngleResolution; - float ex_radius = m_radius * (1.0f + GrabberOffset); + const float step_angle = (float)m_angle / AngleResolution; + const float ex_radius = m_radius * (1.0f + GrabberOffset); ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i <= AngleResolution; ++i) - { - float angle = (float)i * step_angle; - float x = ::cos(angle) * ex_radius; - float y = ::sin(angle) * ex_radius; - float z = 0.0f; + for (unsigned int i = 0; i <= AngleResolution; ++i) { + const float angle = (float)i * step_angle; + const float x = ::cos(angle) * ex_radius; + const float y = ::sin(angle) * ex_radius; + const float z = 0.0f; ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); } glsafe(::glEnd()); @@ -305,9 +320,9 @@ void GLGizmoRotate::render_angle() const void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const { - double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); + const double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset); m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); - m_grabbers[0].angles(2) = m_angle; + m_grabbers[0].angles.z() = m_angle; glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color.data() : m_highlight_color.data())); @@ -322,8 +337,8 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool picking) const { - float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); - double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size); + const float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); + const double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size); std::array color = m_grabbers[0].color; if (!picking && m_hover_id != -1) { @@ -367,10 +382,14 @@ void GLGizmoRotate::transform_to_local(const Selection& selection) const { glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); +#if ENABLE_WORLD_COORDINATE + glsafe(::glMultMatrixd(m_orient_matrix.data())); +#else if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) { - Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } +#endif // ENABLE_WORLD_COORDINATE switch (m_axis) { @@ -423,8 +442,12 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons } } +#if ENABLE_WORLD_COORDINATE + m = m * m_orient_matrix.inverse(); +#else if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse(); +#endif // ENABLE_WORLD_COORDINATE m.translate(-m_center); @@ -473,13 +496,13 @@ bool GLGizmoRotate3D::on_is_activable() const void GLGizmoRotate3D::on_start_dragging() { - if ((0 <= m_hover_id) && (m_hover_id < 3)) + if (0 <= m_hover_id && m_hover_id < 3) m_gizmos[m_hover_id].start_dragging(); } void GLGizmoRotate3D::on_stop_dragging() { - if ((0 <= m_hover_id) && (m_hover_id < 3)) + if (0 <= m_hover_id && m_hover_id < 3) m_gizmos[m_hover_id].stop_dragging(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 3245c4dbe8..1ef4f8c23c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -30,15 +30,16 @@ public: private: Axis m_axis; - double m_angle; - - mutable Vec3d m_center; - mutable float m_radius; - - mutable float m_snap_coarse_in_radius; - mutable float m_snap_coarse_out_radius; - mutable float m_snap_fine_in_radius; - mutable float m_snap_fine_out_radius; + double m_angle{ 0.0 }; + Vec3d m_center{ Vec3d::Zero() }; + float m_radius{ 0.0 }; + float m_snap_coarse_in_radius{ 0.0 }; + float m_snap_coarse_out_radius{ 0.0 }; + float m_snap_fine_in_radius{ 0.0 }; + float m_snap_fine_out_radius{ 0.0 }; +#if ENABLE_WORLD_COORDINATE + Transform3d m_orient_matrix{ Transform3d::Identity() }; +#endif // ENABLE_WORLD_COORDINATE public: GLGizmoRotate(GLCanvas3D& parent, Axis axis); @@ -70,6 +71,10 @@ private: void transform_to_local(const Selection& selection) const; // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const; + +#if ENABLE_WORLD_COORDINATE + void init_data_from_selection(const Selection& selection); +#endif // ENABLE_WORLD_COORDINATE }; class GLGizmoRotate3D : public GLGizmoBase @@ -80,7 +85,7 @@ public: GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); 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_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation.x()); m_gizmos[Y].set_angle(rotation.y()); m_gizmos[Z].set_angle(rotation.z()); } std::string get_tooltip() const override { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index fa90809b91..2e5d864e9a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -640,7 +640,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Rotate: { // Apply new temporary rotations +#if ENABLE_WORLD_COORDINATE + TransformationType transformation_type(wxGetApp().obj_manipul()->get_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint); +#else TransformationType transformation_type(TransformationType::World_Relative_Joint); +#endif // ENABLE_WORLD_COORDINATE if (evt.AltDown()) transformation_type.set_independent(); selection.rotate(get_rotation(), transformation_type); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index cace704914..8dd169a62f 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -741,7 +741,7 @@ void Selection::translate(const Vec3d& displacement, bool local) #if !DISABLE_INSTANCES_SYNCH if (translation_type == Instance) - synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SyncRotationType::NONE); else if (translation_type == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH @@ -793,11 +793,22 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; const Vec3d &rotation = first_volume.get_instance_rotation(); const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); - volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + volume.set_instance_rotation(Vec3d(rotation.x(), rotation.y(), rotation.z() + z_diff)); } else { // extracts rotations from the composed transformation - Vec3d new_rotation = transformation_type.world() ? +#if ENABLE_WORLD_COORDINATE + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Vec3d new_rotation = transformation_type.world() ? + Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()) : + transformation_type.absolute() ? rotation : Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m); + if (rot_axis_max == 2 && transformation_type.world() && transformation_type.joint()) { + // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. + const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); + volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + } +#else + const Vec3d new_rotation = transformation_type.world() ? Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) : transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); if (rot_axis_max == 2 && transformation_type.joint()) { @@ -805,6 +816,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); } +#endif // ENABLE_WORLD_COORDINATE volume.set_instance_rotation(new_rotation); object_instance_first[volume.object_idx()] = i; } @@ -823,14 +835,13 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ v.set_volume_rotation(new_rotation); } } - else - { + else { if (m_mode == Instance) rotate_instance(v, i); else if (m_mode == Volume) { // extracts rotations from the composed transformation - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); if (transformation_type.joint()) { const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); @@ -842,12 +853,25 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ } } - #if !DISABLE_INSTANCES_SYNCH +#if !DISABLE_INSTANCES_SYNCH +#if ENABLE_WORLD_COORDINATE + if (m_mode == Instance) { + SyncRotationType synch; + if (transformation_type.world() && rot_axis_max == 2) + synch = SyncRotationType::NONE; + else if (transformation_type.local()) + synch = SyncRotationType::FULL; + else + synch = SyncRotationType::GENERAL; + synchronize_unselected_instances(synch); + } +#else if (m_mode == Instance) synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); +#endif // ENABLE_WORLD_COORDINATE else if (m_mode == Volume) synchronize_unselected_volumes(); - #endif // !DISABLE_INSTANCES_SYNCH +#endif // !DISABLE_INSTANCES_SYNCH } else { // it's the wipe tower that's selected and being rotated GLVolume& volume = *((*m_volumes)[*m_list.begin()]); // the wipe tower is always alone in the selection @@ -855,7 +879,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // make sure the wipe tower rotates around its center, not origin // we can assume that only Z rotation changes const Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset(); - const Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0.0, 0.0, 1.0)) * center_local; + const Vec3d center_local_new = Eigen::AngleAxisd(rotation.z()-volume.get_volume_rotation().z(), Vec3d::UnitZ()) * center_local; volume.set_volume_rotation(rotation); volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); } @@ -889,7 +913,7 @@ void Selection::flattening_rotate(const Vec3d& normal) // Apply the same transformation also to other instances, // but respect their possibly diffrent z-rotation. if (m_mode == Instance) - synchronize_unselected_instances(SYNC_ROTATION_GENERAL); + synchronize_unselected_instances(SyncRotationType::GENERAL); #endif // !DISABLE_INSTANCES_SYNCH this->set_bounding_boxes_dirty(); @@ -952,7 +976,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SyncRotationType::NONE); else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH @@ -1019,7 +1043,7 @@ void Selection::mirror(Axis axis) #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - synchronize_unselected_instances(SYNC_ROTATION_NONE); + synchronize_unselected_instances(SyncRotationType::NONE); else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH @@ -1359,8 +1383,7 @@ void Selection::copy_to_clipboard() m_clipboard.reset(); - for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content) - { + for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content) { ModelObject* src_object = m_model->objects[object.first]; ModelObject* dst_object = m_clipboard.add_object(); dst_object->name = src_object->name; @@ -1373,26 +1396,22 @@ void Selection::copy_to_clipboard() dst_object->layer_height_profile.assign(src_object->layer_height_profile); dst_object->origin_translation = src_object->origin_translation; - for (int i : object.second) - { + for (int i : object.second) { dst_object->add_instance(*src_object->instances[i]); } - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { // Copy the ModelVolumes only for the selected GLVolumes of the 1st selected instance. const GLVolume* volume = (*m_volumes)[i]; - if ((volume->object_idx() == object.first) && (volume->instance_idx() == *object.second.begin())) - { + if (volume->object_idx() == object.first && volume->instance_idx() == *object.second.begin()) { int volume_idx = volume->volume_idx(); - if ((0 <= volume_idx) && (volume_idx < (int)src_object->volumes.size())) - { + if (0 <= volume_idx && volume_idx < (int)src_object->volumes.size()) { ModelVolume* src_volume = src_object->volumes[volume_idx]; ModelVolume* dst_volume = dst_object->add_volume(*src_volume); dst_volume->set_new_unique_id(); - } else { - assert(false); } + else + assert(false); } } } @@ -1428,8 +1447,7 @@ std::vector Selection::get_volume_idxs_from_object(unsigned int ob { std::vector idxs; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { if ((*m_volumes)[i]->object_idx() == (int)object_idx) idxs.push_back(i); } @@ -1441,10 +1459,9 @@ std::vector Selection::get_volume_idxs_from_instance(unsigned int { std::vector idxs; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) + if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) idxs.push_back(i); } @@ -1458,9 +1475,8 @@ std::vector Selection::get_volume_idxs_from_volume(unsigned int ob for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) - { - if (((int)instance_idx != -1) && (v->instance_idx() == (int)instance_idx)) + if (v->object_idx() == (int)object_idx && v->volume_idx() == (int)volume_idx) { + if ((int)instance_idx != -1 && v->instance_idx() == (int)instance_idx) idxs.push_back(i); } } @@ -1472,8 +1488,7 @@ std::vector Selection::get_missing_volume_idxs_from(const std::vec { std::vector idxs; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { std::vector::const_iterator it = std::find(volume_idxs.begin(), volume_idxs.end(), i); if (it == volume_idxs.end()) idxs.push_back(i); @@ -1486,8 +1501,7 @@ std::vector Selection::get_unselected_volume_idxs_from(const std:: { std::vector idxs; - for (unsigned int i : volume_idxs) - { + for (unsigned int i : volume_idxs) { if (m_list.find(i) == m_list.end()) idxs.push_back(i); } @@ -1505,8 +1519,7 @@ void Selection::update_type() m_cache.content.clear(); m_type = Mixed; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* volume = (*m_volumes)[i]; int obj_idx = volume->object_idx(); int inst_idx = volume->instance_idx(); @@ -1525,23 +1538,19 @@ void Selection::update_type() { if (m_list.empty()) m_type = Empty; - else if (m_list.size() == 1) - { + else if (m_list.size() == 1) { const GLVolume* first = (*m_volumes)[*m_list.begin()]; if (first->is_wipe_tower) m_type = WipeTower; - else if (first->is_modifier) - { + else if (first->is_modifier) { m_type = SingleModifier; requires_disable = true; } - else - { + else { const ModelObject* model_object = m_model->objects[first->object_idx()]; unsigned int volumes_count = (unsigned int)model_object->volumes.size(); unsigned int instances_count = (unsigned int)model_object->instances.size(); - if (volumes_count * instances_count == 1) - { + if (volumes_count * instances_count == 1) { m_type = SingleFullObject; // ensures the correct mode is selected m_mode = Instance; @@ -1552,15 +1561,13 @@ void Selection::update_type() // ensures the correct mode is selected m_mode = Instance; } - else - { + else { m_type = SingleVolume; requires_disable = true; } } } - else - { + else { unsigned int sla_volumes_count = 0; // Note: sla_volumes_count is a count of the selected sla_volumes per object instead of per instance, like a model_volumes_count is for (unsigned int i : m_list) { @@ -1575,25 +1582,20 @@ void Selection::update_type() unsigned int instances_count = (unsigned int)model_object->instances.size(); unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); - if (model_volumes_count * instances_count + sla_volumes_count == (unsigned int)m_list.size()) - { + if (model_volumes_count * instances_count + sla_volumes_count == (unsigned int)m_list.size()) { m_type = SingleFullObject; // ensures the correct mode is selected m_mode = Instance; } - else if (selected_instances_count == 1) - { - if (model_volumes_count + sla_volumes_count == (unsigned int)m_list.size()) - { + else if (selected_instances_count == 1) { + if (model_volumes_count + sla_volumes_count == (unsigned int)m_list.size()) { m_type = SingleFullInstance; // ensures the correct mode is selected m_mode = Instance; } - else - { + else { unsigned int modifiers_count = 0; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { if ((*m_volumes)[i]->is_modifier) ++modifiers_count; } @@ -1606,25 +1608,21 @@ void Selection::update_type() requires_disable = true; } } - else if ((selected_instances_count > 1) && (selected_instances_count * model_volumes_count + sla_volumes_count == (unsigned int)m_list.size())) - { + else if (selected_instances_count > 1 && selected_instances_count * model_volumes_count + sla_volumes_count == (unsigned int)m_list.size()) { m_type = MultipleFullInstance; // ensures the correct mode is selected m_mode = Instance; } } - else - { + else { unsigned int sels_cntr = 0; - for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) - { + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) { const ModelObject* model_object = m_model->objects[it->first]; unsigned int volumes_count = (unsigned int)model_object->volumes.size(); unsigned int instances_count = (unsigned int)model_object->instances.size(); sels_cntr += volumes_count * instances_count; } - if (sels_cntr + sla_volumes_count == (unsigned int)m_list.size()) - { + if (sels_cntr + sla_volumes_count == (unsigned int)m_list.size()) { m_type = MultipleFullObject; // ensures the correct mode is selected m_mode = Instance; @@ -1635,8 +1633,7 @@ void Selection::update_type() int object_idx = get_object_idx(); int instance_idx = get_instance_idx(); - for (GLVolume* v : *m_volumes) - { + for (GLVolume* v : *m_volumes) { v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false; } @@ -2091,7 +2088,7 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation())); switch (sync_rotation_type) { - case SYNC_ROTATION_NONE: { + case SyncRotationType::NONE: { // z only rotation -> synch instance z // The X,Y rotations should be synchronized from start to end of the rotation. assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); @@ -2099,12 +2096,25 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ v->set_instance_offset(Z, volume->get_instance_offset().z()); break; } - case SYNC_ROTATION_GENERAL: + case SyncRotationType::GENERAL: { // generic rotation -> update instance z with the delta of the rotation. const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() + z_diff }); break; } +#if ENABLE_WORLD_COORDINATE + case SyncRotationType::FULL: { + // generic rotation -> update instance z with the delta of the rotation. + const Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rotation, m_cache.volumes_data[j].get_instance_rotation())); + const Vec3d& axis = angle_axis.axis(); + const double z_diff = (std::abs(axis.x()) > EPSILON || std::abs(axis.y()) > EPSILON) ? + angle_axis.angle() * axis.z() : Geometry::rotation_diff_z(rotation, m_cache.volumes_data[j].get_instance_rotation()); + + v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() + z_diff }); + break; + } +#endif // ENABLE_WORLD_COORDINATE + } v->set_instance_scaling_factor(scaling_factor); v->set_instance_mirror(mirror); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index dea5075114..94ca35eab0 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -368,11 +368,15 @@ private: void render_sidebar_layers_hints(const std::string& sidebar_field) const; public: - enum SyncRotationType { + enum class SyncRotationType { // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis. - SYNC_ROTATION_NONE = 0, + NONE = 0, // Synchronize after rotation by an axis not parallel with Z. - SYNC_ROTATION_GENERAL = 1, + GENERAL = 1, +#if ENABLE_WORLD_COORDINATE + // Fully synchronize rotation. + FULL = 2, +#endif // ENABLE_WORLD_COORDINATE }; void synchronize_unselected_instances(SyncRotationType sync_rotation_type); void synchronize_unselected_volumes(); From e1619944ad8b1e061ed48bf2b70d942a3683a539 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 Oct 2021 13:42:55 +0200 Subject: [PATCH 05/50] Tech ENABLE_WORLD_COORDINATE - Resize Move and Rotate gizmos in dependence of the selected coordinate system --- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 12 ++++++++- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 33 +++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 1 + src/slic3r/GUI/Selection.cpp | 23 ++++++++--------- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index a8df103fcc..b8957f9c57 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -110,8 +110,17 @@ void GLGizmoMove3D::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const Selection& selection = m_parent.get_selection(); - const BoundingBoxf3& box = selection.get_bounding_box(); #if ENABLE_WORLD_COORDINATE + BoundingBoxf3 box; + if (wxGetApp().obj_manipul()->get_world_coordinates()) + box = selection.get_bounding_box(); + else { + const Selection::IndicesList& ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + } + } glsafe(::glPushMatrix()); transform_to_local(selection); @@ -130,6 +139,7 @@ void GLGizmoMove3D::on_render() m_grabbers[2].center = { 0.0, 0.0, half_box_size.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; #else + const BoundingBoxf3& box = selection.get_bounding_box(); const Vec3d& center = box.center(); // x axis diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 5bafc573bd..51831ed2c9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -45,6 +45,10 @@ GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) , m_snap_coarse_out_radius(other.m_snap_coarse_out_radius) , m_snap_fine_in_radius(other.m_snap_fine_in_radius) , m_snap_fine_out_radius(other.m_snap_fine_out_radius) +#if ENABLE_WORLD_COORDINATE + , m_bounding_box(other.m_bounding_box) + , m_orient_matrix(other.m_orient_matrix) +#endif // ENABLE_WORLD_COORDINATE { } @@ -128,7 +132,9 @@ void GLGizmoRotate::on_render() return; const Selection& selection = m_parent.get_selection(); +#if !ENABLE_WORLD_COORDINATE const BoundingBoxf3& box = selection.get_bounding_box(); +#endif // !ENABLE_WORLD_COORDINATE if (m_hover_id != 0 && !m_grabbers[0].dragging) { #if ENABLE_WORLD_COORDINATE @@ -164,8 +170,13 @@ void GLGizmoRotate::on_render() if (m_hover_id != -1) render_angle(); +#if ENABLE_WORLD_COORDINATE + render_grabber(m_bounding_box); + render_grabber_extension(m_bounding_box, false); +#else render_grabber(box); render_grabber_extension(box, false); +#endif // ENABLE_WORLD_COORDINATE glsafe(::glPopMatrix()); } @@ -180,9 +191,14 @@ void GLGizmoRotate::on_render_for_picking() transform_to_local(selection); +#if ENABLE_WORLD_COORDINATE + render_grabbers_for_picking(m_bounding_box); + render_grabber_extension(m_bounding_box, true); +#else const BoundingBoxf3& box = selection.get_bounding_box(); render_grabbers_for_picking(box); render_grabber_extension(box, true); +#endif // ENABLE_WORLD_COORDINATE glsafe(::glPopMatrix()); } @@ -190,9 +206,26 @@ void GLGizmoRotate::on_render_for_picking() #if ENABLE_WORLD_COORDINATE void GLGizmoRotate::init_data_from_selection(const Selection& selection) { +#if ENABLE_WORLD_COORDINATE + m_bounding_box.reset(); + if (wxGetApp().obj_manipul()->get_world_coordinates()) { + m_bounding_box = selection.get_bounding_box(); + m_center = m_bounding_box.center(); + } + else { + const Selection::IndicesList& ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + } + m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix() * m_bounding_box.center(); + } + m_radius = Offset + m_bounding_box.radius(); +#else const BoundingBoxf3& box = selection.get_bounding_box(); m_center = box.center(); m_radius = Offset + box.radius(); +#endif // ENABLE_WORLD_COORDINATE m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; m_snap_fine_in_radius = m_radius; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 1ef4f8c23c..716a1f4aae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -38,6 +38,7 @@ private: float m_snap_fine_in_radius{ 0.0 }; float m_snap_fine_out_radius{ 0.0 }; #if ENABLE_WORLD_COORDINATE + BoundingBoxf3 m_bounding_box; Transform3d m_orient_matrix{ Transform3d::Identity() }; #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 8dd169a62f..95b2d7af15 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1885,7 +1885,8 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { - auto render_sidebar_rotation_hint = [this]() { + auto render_sidebar_rotation_hint = [this](const std::array& color) { + const_cast(&m_curved_arrow)->set_color(-1, color); m_curved_arrow.render(); glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); m_curved_arrow.render(); @@ -1893,18 +1894,14 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) if (boost::ends_with(sidebar_field, "x")) { glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); - const_cast(&m_curved_arrow)->set_color(-1, get_color(X)); - render_sidebar_rotation_hint(); + render_sidebar_rotation_hint(get_color(X)); } else if (boost::ends_with(sidebar_field, "y")) { glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); - const_cast(&m_curved_arrow)->set_color(-1, get_color(Y)); - render_sidebar_rotation_hint(); - } - else if (boost::ends_with(sidebar_field, "z")) { - const_cast(&m_curved_arrow)->set_color(-1, get_color(Z)); - render_sidebar_rotation_hint(); + render_sidebar_rotation_hint(get_color(Y)); } + else if (boost::ends_with(sidebar_field, "z")) + render_sidebar_rotation_hint(get_color(Z)); } void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const @@ -1977,10 +1974,10 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co const BoundingBoxf3& box = get_bounding_box(); - const float min_x = box.min(0) - Margin; - const float max_x = box.max(0) + Margin; - const float min_y = box.min(1) - Margin; - const float max_y = box.max(1) + Margin; + 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; // view dependend order of rendering to keep correct transparency bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); From 34ca3d086e09a9a13b0a62c40455f19b6862f6c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 Oct 2021 15:33:03 +0200 Subject: [PATCH 06/50] Tech ENABLE_WORLD_COORDINATE - Fixed drop to bed button behavior --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 39 ++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 1c07c5c42c..cf3d962453 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -328,27 +328,58 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : Selection& selection = canvas->get_selection(); if (selection.is_single_volume() || selection.is_single_modifier()) { +#if ENABLE_WORLD_COORDINATE const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const double min_z = get_volume_min_z(*volume); + if (!m_world_coordinates) { + const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ()); - const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); - const Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(*volume)); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); + change_position_value(0, diff.x()); + change_position_value(1, diff.y()); + change_position_value(2, diff.z()); + } + else { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); + change_position_value(2, m_cache.position.z() - min_z); + } +#else + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (get_volume_min_z(*volume) * Vec3d::UnitZ()); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); change_position_value(0, diff.x()); change_position_value(1, diff.y()); change_position_value(2, diff.z()); +#endif // ENABLE_WORLD_COORDINATE } else if (selection.is_single_full_instance()) { +#if ENABLE_WORLD_COORDINATE + const double min_z = selection.get_scaled_instance_bounding_box().min.z(); + if (!m_world_coordinates) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ()); + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); + change_position_value(0, diff.x()); + change_position_value(1, diff.y()); + change_position_value(2, diff.z()); + } + else { +#else const ModelObjectPtrs& objects = wxGetApp().model().objects; const int idx = selection.get_object_idx(); if (0 <= idx && idx < static_cast(objects.size())) { const ModelObject* mo = wxGetApp().model().objects[idx]; const double min_z = mo->bounding_box().min.z(); if (std::abs(min_z) > SINKING_Z_THRESHOLD) { +#endif // ENABLE_WORLD_COORDINATE Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); change_position_value(2, m_cache.position.z() - min_z); } +#if !ENABLE_WORLD_COORDINATE } +#endif // !ENABLE_WORLD_COORDINATE } }); editors_grid_sizer->Add(m_drop_to_bed_button); @@ -733,7 +764,7 @@ void ObjectManipulation::update_reset_buttons_visibility() #endif // ENABLE_WORLD_COORDINATE rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); - min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z(); + min_z = selection.get_scaled_instance_bounding_box().min.z(); } else { rotation = volume->get_volume_rotation(); @@ -743,7 +774,7 @@ void ObjectManipulation::update_reset_buttons_visibility() show_rotation = !rotation.isApprox(Vec3d::Zero()); show_scale = !scale.isApprox(Vec3d::Ones()); #if ENABLE_WORLD_COORDINATE - show_drop_to_bed = min_z > EPSILON; + show_drop_to_bed = min_z < SINKING_Z_THRESHOLD; #else show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; #endif // ENABLE_WORLD_COORDINATE From 095f0ee71e26aa71a1d035455dc65c366a269de4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 Oct 2021 15:41:02 +0200 Subject: [PATCH 07/50] Fixed build when tech ENABLE_WORLD_COORDINATE is disabled --- src/slic3r/GUI/Selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 95b2d7af15..d86ea5c137 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -867,7 +867,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ } #else if (m_mode == Instance) - synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); + synchronize_unselected_instances((rot_axis_max == 2) ? SyncRotationType::NONE : SyncRotationType::GENERAL); #endif // ENABLE_WORLD_COORDINATE else if (m_mode == Volume) synchronize_unselected_volumes(); From d9b3386d08004ee1b1a4b2be4b47bfdfc5a71a2e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 Oct 2021 09:03:35 +0200 Subject: [PATCH 08/50] Tech ENABLE_WORLD_COORDINATE - Fixes in Gizmo Move behavior --- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 47 +++++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 1 + 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index b8957f9c57..0d9349948e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -73,14 +73,21 @@ void GLGizmoMove3D::on_start_dragging() { if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); - const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); #if ENABLE_WORLD_COORDINATE - const Vec3d center = box.center(); + const BoundingBoxf3 box = get_selection_box(); + Vec3d center; + if (wxGetApp().obj_manipul()->get_world_coordinates()) + center = box.center(); + else { + const Selection& selection = m_parent.get_selection(); + center = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix() * box.center(); + } m_starting_drag_position = center + m_grabbers[m_hover_id].center; m_starting_box_center = center; m_starting_box_bottom_center = center; m_starting_box_bottom_center.z() = box.min.z(); #else + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); @@ -109,22 +116,12 @@ void GLGizmoMove3D::on_render() glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); - const Selection& selection = m_parent.get_selection(); #if ENABLE_WORLD_COORDINATE - BoundingBoxf3 box; - if (wxGetApp().obj_manipul()->get_world_coordinates()) - box = selection.get_bounding_box(); - else { - const Selection::IndicesList& ids = selection.get_volume_idxs(); - for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); - } - } glsafe(::glPushMatrix()); - transform_to_local(selection); + transform_to_local(m_parent.get_selection()); const Vec3d zero = Vec3d::Zero(); + BoundingBoxf3 box = get_selection_box(); const Vec3d half_box_size = 0.5 * box.size(); // x axis @@ -139,6 +136,7 @@ void GLGizmoMove3D::on_render() m_grabbers[2].center = { 0.0, 0.0, half_box_size.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; #else + const Selection& selection = m_parent.get_selection(); const BoundingBoxf3& box = selection.get_bounding_box(); const Vec3d& center = box.center(); @@ -215,10 +213,9 @@ void GLGizmoMove3D::on_render_for_picking() glsafe(::glDisable(GL_DEPTH_TEST)); #if ENABLE_WORLD_COORDINATE - const Selection& selection = m_parent.get_selection(); - const BoundingBoxf3& box = selection.get_bounding_box(); glsafe(::glPushMatrix()); - transform_to_local(selection); + transform_to_local(m_parent.get_selection()); + const BoundingBoxf3 box = get_selection_box(); #else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); #endif // ENABLE_WORLD_COORDINATE @@ -310,6 +307,22 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const glsafe(::glMultMatrixd(orient_matrix.data())); } } + +BoundingBoxf3 GLGizmoMove3D::get_selection_box() +{ + const Selection& selection = m_parent.get_selection(); + BoundingBoxf3 box; + if (wxGetApp().obj_manipul()->get_world_coordinates()) + box = selection.get_bounding_box(); + else { + const Selection::IndicesList& ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + } + } + return box; +} #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 4a44b3c95e..8f9f6ea271 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -44,6 +44,7 @@ private: void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const; #if ENABLE_WORLD_COORDINATE void transform_to_local(const Selection& selection) const; + BoundingBoxf3 get_selection_box(); #endif // ENABLE_WORLD_COORDINATE }; From 8c30ddc54f67eabfa0ff1f4ce11da369b541380b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 Oct 2021 13:11:59 +0200 Subject: [PATCH 09/50] Tech ENABLE_WORLD_COORDINATE - Fixed Move and Rotate Gizmo size when the selected instance is scaled --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 3 +-- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 393be1a4e8..556d8c1267 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -205,8 +205,7 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { if (m_grabbers[i].enabled) { - std::array color = picking_color_component(i); - m_grabbers[i].color = color; + m_grabbers[i].color = picking_color_component(i); m_grabbers[i].render_for_picking(mean_size); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 0d9349948e..f586d312d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -318,7 +318,7 @@ BoundingBoxf3 GLGizmoMove3D::get_selection_box() const Selection::IndicesList& ids = selection.get_volume_idxs(); for (unsigned int id : ids) { const GLVolume* v = selection.get_volume(id); - box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); } } return box; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 51831ed2c9..3db0e3ddb2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -216,9 +216,9 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) const Selection::IndicesList& ids = selection.get_volume_idxs(); for (unsigned int id : ids) { const GLVolume* v = selection.get_volume(id); - m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); } - m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix() * m_bounding_box.center(); + m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); } m_radius = Offset + m_bounding_box.radius(); #else From 63e5c0c5a112b2dbb4ff424904fc0d2fb6f1a767 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 Oct 2021 14:00:00 +0200 Subject: [PATCH 10/50] Refactoring into GLGizmoScale3D --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 140 +++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 18 ++-- 2 files changed, 70 insertions(+), 88 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index b97507166b..812259a835 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -11,13 +11,10 @@ namespace Slic3r { namespace GUI { -const float GLGizmoScale3D::Offset = 5.0f; +const double GLGizmoScale3D::Offset = 5.0; GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_scale(Vec3d::Ones()) - , m_offset(Vec3d::Zero()) - , m_snap_step(0.05) { } @@ -25,27 +22,24 @@ std::string GLGizmoScale3D::get_tooltip() const { const Selection& selection = m_parent.get_selection(); - bool single_instance = selection.is_single_full_instance(); - bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); - - Vec3f scale = 100.0f * Vec3f::Ones(); - if (single_instance) - scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast(); - else if (single_volume) - scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast(); + Vec3d scale = 100.0 * Vec3d::Ones(); + if (selection.is_single_full_instance()) + scale = 100.0 * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor(); + else if (selection.is_single_modifier() || selection.is_single_volume()) + scale = 100.0 * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor(); if (m_hover_id == 0 || m_hover_id == 1 || m_grabbers[0].dragging || m_grabbers[1].dragging) - return "X: " + format(scale(0), 4) + "%"; + return "X: " + format(scale.x(), 4) + "%"; else if (m_hover_id == 2 || m_hover_id == 3 || m_grabbers[2].dragging || m_grabbers[3].dragging) - return "Y: " + format(scale(1), 4) + "%"; + return "Y: " + format(scale.y(), 4) + "%"; else if (m_hover_id == 4 || m_hover_id == 5 || m_grabbers[4].dragging || m_grabbers[5].dragging) - return "Z: " + format(scale(2), 4) + "%"; + return "Z: " + format(scale.z(), 4) + "%"; else if (m_hover_id == 6 || m_hover_id == 7 || m_hover_id == 8 || m_hover_id == 9 || m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging) { - std::string tooltip = "X: " + format(scale(0), 4) + "%\n"; - tooltip += "Y: " + format(scale(1), 4) + "%\n"; - tooltip += "Z: " + format(scale(2), 4) + "%"; + std::string tooltip = "X: " + format(scale.x(), 4) + "%\n"; + tooltip += "Y: " + format(scale.y(), 4) + "%\n"; + tooltip += "Z: " + format(scale.z(), 4) + "%"; return tooltip; } else @@ -54,20 +48,19 @@ std::string GLGizmoScale3D::get_tooltip() const bool GLGizmoScale3D::on_init() { - for (int i = 0; i < 10; ++i) - { + for (int i = 0; i < 10; ++i) { m_grabbers.push_back(Grabber()); } double half_pi = 0.5 * (double)PI; // x axis - m_grabbers[0].angles(1) = half_pi; - m_grabbers[1].angles(1) = half_pi; + m_grabbers[0].angles.y() = half_pi; + m_grabbers[1].angles.y() = half_pi; // y axis - m_grabbers[2].angles(0) = half_pi; - m_grabbers[3].angles(0) = half_pi; + m_grabbers[2].angles.x() = half_pi; + m_grabbers[3].angles.x() = half_pi; m_shortcut_key = WXK_CONTROL_S; @@ -87,29 +80,28 @@ bool GLGizmoScale3D::on_is_activable() const void GLGizmoScale3D::on_start_dragging() { - if (m_hover_id != -1) - { + if (m_hover_id != -1) { m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box(); const Vec3d& center = m_starting.box.center(); - m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2)); - m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min(0), center(1), center(2)); - m_starting.pivots[2] = m_transform * Vec3d(center(0), m_starting.box.max(1), center(2)); - m_starting.pivots[3] = m_transform * Vec3d(center(0), m_starting.box.min(1), center(2)); - m_starting.pivots[4] = m_transform * Vec3d(center(0), center(1), m_starting.box.max(2)); - m_starting.pivots[5] = m_transform * Vec3d(center(0), center(1), m_starting.box.min(2)); + m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z()); + m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min.x(), center.y(), center.z()); + m_starting.pivots[2] = m_transform * Vec3d(center.x(), m_starting.box.max.y(), center.z()); + m_starting.pivots[3] = m_transform * Vec3d(center.x(), m_starting.box.min.y(), center.z()); + m_starting.pivots[4] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.max.z()); + m_starting.pivots[5] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.min.z()); } } void GLGizmoScale3D::on_update(const UpdateData& data) { - if ((m_hover_id == 0) || (m_hover_id == 1)) + if (m_hover_id == 0 || m_hover_id == 1) do_scale_along_axis(X, data); - else if ((m_hover_id == 2) || (m_hover_id == 3)) + else if (m_hover_id == 2 || m_hover_id == 3) do_scale_along_axis(Y, data); - else if ((m_hover_id == 4) || (m_hover_id == 5)) + else if (m_hover_id == 4 || m_hover_id == 5) do_scale_along_axis(Z, data); else if (m_hover_id >= 6) do_scale_uniform(data); @@ -119,9 +111,6 @@ void GLGizmoScale3D::on_render() { const Selection& selection = m_parent.get_selection(); - bool single_instance = selection.is_single_full_instance(); - bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -132,7 +121,7 @@ void GLGizmoScale3D::on_render() m_offsets_transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); - if (single_instance) { + if (selection.is_single_full_instance()) { // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { @@ -149,7 +138,7 @@ void GLGizmoScale3D::on_render() offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); m_offsets_transform = offsets_transform; } - else if (single_volume) { + else if (selection.is_single_modifier() || selection.is_single_volume()) { const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); m_box = v->bounding_box(); m_transform = v->world_matrix(); @@ -162,35 +151,35 @@ void GLGizmoScale3D::on_render() m_box = selection.get_bounding_box(); const Vec3d& center = m_box.center(); - Vec3d offset_x = offsets_transform * Vec3d((double)Offset, 0.0, 0.0); - Vec3d offset_y = offsets_transform * Vec3d(0.0, (double)Offset, 0.0); - Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, (double)Offset); + Vec3d offset_x = offsets_transform * Vec3d(Offset, 0.0, 0.0); + Vec3d offset_y = offsets_transform * Vec3d(0.0, Offset, 0.0); + Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, Offset); bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); // x axis - m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x; - m_grabbers[0].color = (ctrl_down && (m_hover_id == 1)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; - m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x; - m_grabbers[1].color = (ctrl_down && (m_hover_id == 0)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), center.z()) - offset_x; + m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), center.z()) + offset_x; + m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; // y axis - m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y; - m_grabbers[2].color = (ctrl_down && (m_hover_id == 3)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; - m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y; - m_grabbers[3].color = (ctrl_down && (m_hover_id == 2)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), center.z()) - offset_y; + m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), center.z()) + offset_y; + m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; // z axis - m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z; - m_grabbers[4].color = (ctrl_down && (m_hover_id == 5)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; - m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z; - m_grabbers[5].color = (ctrl_down && (m_hover_id == 4)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()) - offset_z; + m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()) + offset_z; + m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; // uniform - m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y; - m_grabbers[7].center = m_transform * Vec3d(m_box.max(0), m_box.min(1), center(2)) + offset_x - offset_y; - m_grabbers[8].center = m_transform * Vec3d(m_box.max(0), m_box.max(1), center(2)) + offset_x + offset_y; - m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y; + m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; + m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; + m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; + m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } @@ -204,7 +193,7 @@ void GLGizmoScale3D::on_render() const BoundingBoxf3& selection_box = selection.get_bounding_box(); - float grabber_mean_size = (float)((selection_box.size()(0) + selection_box.size()(1) + selection_box.size()(2)) / 3.0); + float grabber_mean_size = (float)((selection_box.size().x() + selection_box.size().y() + selection_box.size().z()) / 3.0); if (m_hover_id == -1) { // draw connections @@ -303,8 +292,7 @@ void GLGizmoScale3D::on_render_for_picking() void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const { unsigned int grabbers_count = (unsigned int)m_grabbers.size(); - if ((id_1 < grabbers_count) && (id_2 < grabbers_count)) - { + if (id_1 < grabbers_count && id_2 < grabbers_count) { ::glBegin(GL_LINES); ::glVertex3dv(m_grabbers[id_1].center.data()); ::glVertex3dv(m_grabbers[id_2].center.data()); @@ -314,12 +302,10 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) { - double ratio = calc_ratio(data); - if (ratio > 0.0) - { + const double ratio = calc_ratio(data); + if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; - if (m_starting.ctrl_down) - { + if (m_starting.ctrl_down) { double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis); if (m_hover_id == 2 * axis) local_offset *= -1.0; @@ -342,9 +328,8 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) { - double ratio = calc_ratio(data); - if (ratio > 0.0) - { + const double ratio = calc_ratio(data); + if (ratio > 0.0) { m_scale = m_starting.scale * ratio; m_offset = Vec3d::Zero(); } @@ -354,23 +339,22 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const { double ratio = 0.0; - Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.pivots[m_hover_id] : m_starting.box.center(); + const Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.pivots[m_hover_id] : m_starting.box.center(); - Vec3d starting_vec = m_starting.drag_position - pivot; - double len_starting_vec = starting_vec.norm(); - if (len_starting_vec != 0.0) - { + const Vec3d starting_vec = m_starting.drag_position - pivot; + const double len_starting_vec = starting_vec.norm(); + if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.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 - Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_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; + const Vec3d inters_vec = inters - m_starting.drag_position; // finds projection of the vector along the staring direction - double proj = inters_vec.dot(starting_vec.normalized()); + const double proj = inters_vec.dot(starting_vec.normalized()); ratio = (len_starting_vec + proj) / len_starting_vec; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 35615df4a7..46ecd0f834 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -11,26 +11,24 @@ namespace GUI { class GLGizmoScale3D : public GLGizmoBase { - static const float Offset; + static const double Offset; struct StartingData { - Vec3d scale; - Vec3d drag_position; + bool ctrl_down{ false }; + Vec3d scale{ Vec3d::Ones() }; + Vec3d drag_position{ Vec3d::Zero() }; BoundingBoxf3 box; - Vec3d pivots[6]; - bool ctrl_down; - - StartingData() : scale(Vec3d::Ones()), drag_position(Vec3d::Zero()), ctrl_down(false) { for (int i = 0; i < 5; ++i) { pivots[i] = Vec3d::Zero(); } } + std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; }; mutable BoundingBoxf3 m_box; mutable Transform3d m_transform; // Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes) mutable Transform3d m_offsets_transform; - Vec3d m_scale; - Vec3d m_offset; - double m_snap_step; + Vec3d m_scale{ Vec3d::Ones() }; + Vec3d m_offset{ Vec3d::Zero() }; + double m_snap_step{ 0.05 }; StartingData m_starting; public: From 934da6d793da565828b38e0a95750aaa1cdab4b0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 Oct 2021 14:15:20 +0200 Subject: [PATCH 11/50] Another small refactoring into GLGizmoScale3D --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 812259a835..65619676b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -151,35 +151,32 @@ void GLGizmoScale3D::on_render() m_box = selection.get_bounding_box(); const Vec3d& center = m_box.center(); - Vec3d offset_x = offsets_transform * Vec3d(Offset, 0.0, 0.0); - Vec3d offset_y = offsets_transform * Vec3d(0.0, Offset, 0.0); - Vec3d offset_z = offsets_transform * Vec3d(0.0, 0.0, Offset); bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); // x axis - m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), center.z()) - offset_x; + m_grabbers[0].center = m_transform * Vec3d(m_box.min.x() - Offset, center.y(), center.z()); m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; - m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), center.z()) + offset_x; + m_grabbers[1].center = m_transform * Vec3d(m_box.max.x() + Offset, center.y(), center.z()); m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; // y axis - m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), center.z()) - offset_y; + m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y() - Offset, center.z()); m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; - m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), center.z()) + offset_y; + m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y() + Offset, center.z()); m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; // z axis - m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()) - offset_z; + m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z() - Offset); m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; - m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()) + offset_z; + m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z() + Offset); m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; // uniform - m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; - m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; - m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; - m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; + m_grabbers[6].center = m_transform * Vec3d(m_box.min.x() - Offset, m_box.min.y() - Offset, center.z()); + m_grabbers[7].center = m_transform * Vec3d(m_box.max.x() + Offset, m_box.min.y() - Offset, center.z()); + m_grabbers[8].center = m_transform * Vec3d(m_box.max.x() + Offset, m_box.max.y() + Offset, center.z()); + m_grabbers[9].center = m_transform * Vec3d(m_box.min.x() - Offset, m_box.max.y() + Offset, center.z()); for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } @@ -344,7 +341,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const const Vec3d starting_vec = m_starting.drag_position - pivot; const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); + const Vec3d mouse_dir = data.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) From a10bb8e37e64a0c58c4fbc9871939ca928ad1d88 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 13 Oct 2021 14:23:51 +0200 Subject: [PATCH 12/50] Fixed color of the line connecting the grabbers while hovering one grabber and pressing CTRL key in Gizmo Scale --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 65619676b3..c3527440de 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -216,7 +216,7 @@ void GLGizmoScale3D::on_render() } else if (m_hover_id == 0 || m_hover_id == 1) { // draw connection - glsafe(::glColor4fv(m_grabbers[0].color.data())); + glsafe(::glColor4fv(AXES_COLOR[0].data())); render_grabbers_connection(0, 1); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -231,7 +231,7 @@ void GLGizmoScale3D::on_render() } else if (m_hover_id == 2 || m_hover_id == 3) { // draw connection - glsafe(::glColor4fv(m_grabbers[2].color.data())); + glsafe(::glColor4fv(AXES_COLOR[1].data())); render_grabbers_connection(2, 3); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -246,7 +246,7 @@ void GLGizmoScale3D::on_render() } else if (m_hover_id == 4 || m_hover_id == 5) { // draw connection - glsafe(::glColor4fv(m_grabbers[4].color.data())); + glsafe(::glColor4fv(AXES_COLOR[2].data())); render_grabbers_connection(4, 5); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); From 7e5c214b91ae14740fc188413948818a1b49928a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 14 Oct 2021 13:59:27 +0200 Subject: [PATCH 13/50] Other refactoring plus some fixes into GLGizmoScale3D --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 42 ++++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 6 ++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index c3527440de..fd24edfc1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -83,7 +83,7 @@ void GLGizmoScale3D::on_start_dragging() if (m_hover_id != -1) { m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); - m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box(); + m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_box : m_parent.get_selection().get_bounding_box(); const Vec3d& center = m_starting.box.center(); m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z()); @@ -118,15 +118,14 @@ void GLGizmoScale3D::on_render() m_transform = Transform3d::Identity(); // Transforms grabbers' offsets to world refefence system Transform3d offsets_transform = Transform3d::Identity(); - m_offsets_transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); if (selection.is_single_full_instance()) { // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { - const GLVolume* vol = selection.get_volume(idx); - m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix())); + const GLVolume* v = selection.get_volume(idx); + m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); } // gets transform from first selected volume @@ -136,7 +135,6 @@ void GLGizmoScale3D::on_render() angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); - m_offsets_transform = offsets_transform; } else if (selection.is_single_modifier() || selection.is_single_volume()) { const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); @@ -145,38 +143,40 @@ void GLGizmoScale3D::on_render() angles = Geometry::extract_euler_angles(m_transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); - m_offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), v->get_volume_rotation(), Vec3d::Ones(), v->get_volume_mirror()); } else m_box = selection.get_bounding_box(); const Vec3d& center = m_box.center(); + Vec3d offset_x = offsets_transform * (Offset * Vec3d::UnitX()); + Vec3d offset_y = offsets_transform * (Offset * Vec3d::UnitY()); + Vec3d offset_z = offsets_transform * (Offset * Vec3d::UnitZ()); bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); // x axis - m_grabbers[0].center = m_transform * Vec3d(m_box.min.x() - Offset, center.y(), center.z()); + m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), center.z()) - offset_x; m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; - m_grabbers[1].center = m_transform * Vec3d(m_box.max.x() + Offset, center.y(), center.z()); + m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), center.z()) + offset_x; m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; // y axis - m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y() - Offset, center.z()); + m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), center.z()) - offset_y; m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; - m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y() + Offset, center.z()); + m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), center.z()) + offset_y; m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; // z axis - m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z() - Offset); + m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()) - offset_z; m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; - m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z() + Offset); + m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()) + offset_z; m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; // uniform - m_grabbers[6].center = m_transform * Vec3d(m_box.min.x() - Offset, m_box.min.y() - Offset, center.z()); - m_grabbers[7].center = m_transform * Vec3d(m_box.max.x() + Offset, m_box.min.y() - Offset, center.z()); - m_grabbers[8].center = m_transform * Vec3d(m_box.max.x() + Offset, m_box.max.y() + Offset, center.z()); - m_grabbers[9].center = m_transform * Vec3d(m_box.min.x() - Offset, m_box.max.y() + Offset, center.z()); + m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; + m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; + m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; + m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } @@ -303,7 +303,10 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; if (m_starting.ctrl_down) { - double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis); + double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); + if (!m_parent.get_selection().is_single_full_instance()) + local_offset *= m_starting.scale(axis); + if (m_hover_id == 2 * axis) local_offset *= -1.0; @@ -316,7 +319,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: break; } - m_offset = m_offsets_transform * local_offset_vec; + m_offset = local_offset_vec; } else m_offset = Vec3d::Zero(); @@ -336,10 +339,11 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const { double ratio = 0.0; - const Vec3d pivot = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_starting.pivots[m_hover_id] : m_starting.box.center(); + const Vec3d pivot = (m_starting.ctrl_down && m_hover_id < 6) ? m_starting.pivots[m_hover_id] : m_starting.box.center(); const Vec3d starting_vec = m_starting.drag_position - pivot; const double len_starting_vec = starting_vec.norm(); + if (len_starting_vec != 0.0) { const Vec3d mouse_dir = data.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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 46ecd0f834..2112340f64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -22,10 +22,8 @@ class GLGizmoScale3D : public GLGizmoBase std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; }; - mutable BoundingBoxf3 m_box; - mutable Transform3d m_transform; - // Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes) - mutable Transform3d m_offsets_transform; + BoundingBoxf3 m_box; + Transform3d m_transform; Vec3d m_scale{ Vec3d::Ones() }; Vec3d m_offset{ Vec3d::Zero() }; double m_snap_step{ 0.05 }; From 4fdd58e80ad561788d33dd707ae3c85910e4e3a9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 15 Oct 2021 11:58:32 +0200 Subject: [PATCH 14/50] Tech ENABLE_WORLD_COORDINATE - Fixed gizmo Scale in world coordinates --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 142 +++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 11 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 + 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index fd24edfc1e..8fc2e852c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -2,6 +2,9 @@ #include "GLGizmoScale.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" +#if ENABLE_WORLD_COORDINATE +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#endif // ENABLE_WORLD_COORDINATE #include @@ -52,6 +55,7 @@ bool GLGizmoScale3D::on_init() m_grabbers.push_back(Grabber()); } +#if !ENABLE_WORLD_COORDINATE double half_pi = 0.5 * (double)PI; // x axis @@ -61,6 +65,7 @@ bool GLGizmoScale3D::on_init() // y axis m_grabbers[2].angles.x() = half_pi; m_grabbers[3].angles.x() = half_pi; +#endif // !ENABLE_WORLD_COORDINATE m_shortcut_key = WXK_CONTROL_S; @@ -81,17 +86,34 @@ bool GLGizmoScale3D::on_is_activable() const void GLGizmoScale3D::on_start_dragging() { if (m_hover_id != -1) { - m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); +#if ENABLE_WORLD_COORDINATE + m_starting.drag_position = m_grabbers_transform * m_grabbers[m_hover_id].center; + m_starting.box = m_box; + m_starting.center = m_center; + + if (m_starting.ctrl_down) { + const Vec3d center = m_starting.box.center(); + const Transform3d trafo = wxGetApp().obj_manipul()->get_world_coordinates() ? Transform3d::Identity() : m_transform; + m_starting.pivots[0] = trafo * Vec3d(m_starting.box.max.x(), center.y(), center.z()); + m_starting.pivots[1] = trafo * Vec3d(m_starting.box.min.x(), center.y(), center.z()); + m_starting.pivots[2] = trafo * Vec3d(center.x(), m_starting.box.max.y(), center.z()); + m_starting.pivots[3] = trafo * Vec3d(center.x(), m_starting.box.min.y(), center.z()); + m_starting.pivots[4] = trafo * Vec3d(center.x(), center.y(), m_starting.box.max.z()); + m_starting.pivots[5] = trafo * Vec3d(center.x(), center.y(), m_starting.box.min.z()); + } +#else + m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_box : m_parent.get_selection().get_bounding_box(); - const Vec3d& center = m_starting.box.center(); + const Vec3d center = m_starting.box.center(); m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z()); m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min.x(), center.y(), center.z()); m_starting.pivots[2] = m_transform * Vec3d(center.x(), m_starting.box.max.y(), center.z()); m_starting.pivots[3] = m_transform * Vec3d(center.x(), m_starting.box.min.y(), center.z()); m_starting.pivots[4] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.max.z()); m_starting.pivots[5] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.min.z()); +#endif // ENABLE_WORLD_COORDINATE } } @@ -116,81 +138,156 @@ void GLGizmoScale3D::on_render() m_box.reset(); m_transform = Transform3d::Identity(); +#if ENABLE_WORLD_COORDINATE + m_grabbers_transform = Transform3d::Identity(); + + if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) { +#else // Transforms grabbers' offsets to world refefence system Transform3d offsets_transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); if (selection.is_single_full_instance()) { +#endif // !ENABLE_WORLD_COORDINATE + // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { const GLVolume* v = selection.get_volume(idx); +#if ENABLE_WORLD_COORDINATE + m_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); +#else m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); +#endif // ENABLE_WORLD_COORDINATE } // gets transform from first selected volume const GLVolume* v = selection.get_volume(*idxs.begin()); m_transform = v->get_instance_transformation().get_matrix(); +#if ENABLE_WORLD_COORDINATE + m_grabbers_transform = v->get_instance_transformation().get_matrix(true, true, false, true); +#else // gets angles from first selected volume angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); +#endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_WORLD_COORDINATE + else if ((selection.is_single_modifier() || selection.is_single_volume()) && !wxGetApp().obj_manipul()->get_world_coordinates()) { +#else else if (selection.is_single_modifier() || selection.is_single_volume()) { +#endif // ENABLE_WORLD_COORDINATE const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_WORLD_COORDINATE + m_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); +#else m_box = v->bounding_box(); +#endif // ENABLE_WORLD_COORDINATE m_transform = v->world_matrix(); +#if ENABLE_WORLD_COORDINATE + m_grabbers_transform = m_transform; +#else angles = Geometry::extract_euler_angles(m_transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); +#endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_WORLD_COORDINATE + else { + m_box = selection.get_bounding_box(); + m_transform = Geometry::assemble_transform(m_box.center()); + m_grabbers_transform = m_transform; + m_center = selection.is_single_full_instance() ? selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_offset() : m_box.center(); + } +#else else m_box = selection.get_bounding_box(); - const Vec3d& center = m_box.center(); Vec3d offset_x = offsets_transform * (Offset * Vec3d::UnitX()); Vec3d offset_y = offsets_transform * (Offset * Vec3d::UnitY()); Vec3d offset_z = offsets_transform * (Offset * Vec3d::UnitZ()); +#endif // ENABLE_WORLD_COORDINATE - bool ctrl_down = (m_dragging && m_starting.ctrl_down) || (!m_dragging && wxGetKeyState(WXK_CONTROL)); + bool ctrl_down = m_dragging && m_starting.ctrl_down || !m_dragging && wxGetKeyState(WXK_CONTROL); // x axis +#if ENABLE_WORLD_COORDINATE + const Vec3d box_half_size = 0.5 * m_box.size(); + bool use_constrain = ctrl_down && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); + + m_grabbers[0].center = { -(box_half_size.x() + Offset), 0.0, 0.0 }; + m_grabbers[0].color = (use_constrain && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + m_grabbers[1].center = { box_half_size.x() + Offset, 0.0, 0.0 }; + m_grabbers[1].color = (use_constrain && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; +#else + const Vec3d center = m_box.center(); + m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), center.z()) - offset_x; m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), center.z()) + offset_x; m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; +#endif // ENABLE_WORLD_COORDINATE // y axis +#if ENABLE_WORLD_COORDINATE + m_grabbers[2].center = { 0.0, -(box_half_size.y() + Offset), 0.0 }; + m_grabbers[2].color = (use_constrain && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + m_grabbers[3].center = { 0.0, box_half_size.y() + Offset, 0.0 }; + m_grabbers[3].color = (use_constrain && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; +#else m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), center.z()) - offset_y; m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), center.z()) + offset_y; m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; +#endif // ENABLE_WORLD_COORDINATE // z axis +#if ENABLE_WORLD_COORDINATE + m_grabbers[4].center = { 0.0, 0.0, -(box_half_size.z() + Offset) }; + m_grabbers[4].color = (use_constrain && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + m_grabbers[5].center = { 0.0, 0.0, box_half_size.z() + Offset }; + m_grabbers[5].color = (use_constrain && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; +#else m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()) - offset_z; m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()) + offset_z; m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; +#endif // ENABLE_WORLD_COORDINATE // uniform +#if ENABLE_WORLD_COORDINATE + m_grabbers[6].center = { -(box_half_size.x() + Offset), -(box_half_size.y() + Offset), 0.0 }; + m_grabbers[7].center = { box_half_size.x() + Offset, -(box_half_size.y() + Offset), 0.0 }; + m_grabbers[8].center = { box_half_size.x() + Offset, box_half_size.y() + Offset, 0.0 }; + m_grabbers[9].center = { -(box_half_size.x() + Offset), box_half_size.y() + Offset, 0.0 }; +#else m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; +#endif // ENABLE_WORLD_COORDINATE for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } +#if !ENABLE_WORLD_COORDINATE // sets grabbers orientation for (int i = 0; i < 10; ++i) { m_grabbers[i].angles = angles; } +#endif // !ENABLE_WORLD_COORDINATE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); +#if ENABLE_WORLD_COORDINATE + glsafe(::glPushMatrix()); + transform_to_local(selection); + float grabber_mean_size = (float)((m_box.size().x() + m_box.size().y() + m_box.size().z()) / 3.0); +#else const BoundingBoxf3& selection_box = selection.get_bounding_box(); - float grabber_mean_size = (float)((selection_box.size().x() + selection_box.size().y() + selection_box.size().z()) / 3.0); +#endif // ENABLE_WORLD_COORDINATE if (m_hover_id == -1) { // draw connections @@ -278,12 +375,23 @@ void GLGizmoScale3D::on_render() shader->stop_using(); } } + +#if ENABLE_WORLD_COORDINATE + glsafe(::glPopMatrix()); +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoScale3D::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); +#if ENABLE_WORLD_COORDINATE + glsafe(::glPushMatrix()); + transform_to_local(m_parent.get_selection()); + render_grabbers_for_picking(m_box); + glsafe(::glPopMatrix()); +#else render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const @@ -299,13 +407,24 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) { +#if ENABLE_WORLD_COORDINATE + double ratio = calc_ratio(data); +#else const double ratio = calc_ratio(data); +#endif // ENABLE_WORLD_COORDINATE if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; if (m_starting.ctrl_down) { +#if ENABLE_WORLD_COORDINATE + const double len_starting_vec = std::abs(m_starting.box.center()(axis) - m_starting.pivots[m_hover_id](axis)); + const double len_center_vec = std::abs(m_starting.center(axis) - m_starting.pivots[m_hover_id](axis)); + const double inner_ratio = len_center_vec / len_starting_vec; + double local_offset = inner_ratio * 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); +#else double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); if (!m_parent.get_selection().is_single_full_instance()) local_offset *= m_starting.scale(axis); +#endif // ENABLE_WORLD_COORDINATE if (m_hover_id == 2 * axis) local_offset *= -1.0; @@ -366,5 +485,18 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const return ratio; } +#if ENABLE_WORLD_COORDINATE +void GLGizmoScale3D::transform_to_local(const Selection& selection) const +{ + const Vec3d center = selection.get_bounding_box().center(); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + + if (!wxGetApp().obj_manipul()->get_world_coordinates()) { + const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } +} +#endif // ENABLE_WORLD_COORDINATE + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 2112340f64..639e4a6cb3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -5,7 +5,6 @@ #include "libslic3r/BoundingBox.hpp" - namespace Slic3r { namespace GUI { @@ -18,12 +17,19 @@ class GLGizmoScale3D : public GLGizmoBase bool ctrl_down{ false }; Vec3d scale{ Vec3d::Ones() }; Vec3d drag_position{ Vec3d::Zero() }; +#if ENABLE_WORLD_COORDINATE + Vec3d center{ Vec3d::Zero() }; +#endif // ENABLE_WORLD_COORDINATE BoundingBoxf3 box; std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; }; BoundingBoxf3 m_box; Transform3d m_transform; +#if ENABLE_WORLD_COORDINATE + Transform3d m_grabbers_transform; + Vec3d m_center{ Vec3d::Zero() }; +#endif // ENABLE_WORLD_COORDINATE Vec3d m_scale{ Vec3d::Ones() }; Vec3d m_offset{ Vec3d::Zero() }; double m_snap_step{ 0.05 }; @@ -58,6 +64,9 @@ private: void do_scale_uniform(const UpdateData& data); double calc_ratio(const UpdateData& data) const; +#if ENABLE_WORLD_COORDINATE + void transform_to_local(const Selection& selection) const; +#endif // ENABLE_WORLD_COORDINATE }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 2e5d864e9a..de7bdbcb6e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -633,7 +633,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) transformation_type.set_independent(); selection.scale(get_scale(), transformation_type); if (control_down) +#if ENABLE_WORLD_COORDINATE + selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->get_world_coordinates()); +#else selection.translate(get_scale_offset(), true); +#endif // ENABLE_WORLD_COORDINATE wxGetApp().obj_manipul()->set_dirty(); break; } From 5649151444412f55a33e5316cf3919032f1a8c53 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 18 Oct 2021 15:13:47 +0200 Subject: [PATCH 15/50] Tech ENABLE_WORLD_COORDINATE - Fixed volumes rotation in world coordinate Added sub-tech ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET which enable showing world coordinates of volumes' offset relative to the instance containing them Show 'Drop to bed' button in sidebar whenever the selected instance or volume is not laying on the printbed --- src/libslic3r/Geometry.cpp | 2 +- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 19 +++++++++++++++++-- src/slic3r/GUI/Selection.cpp | 22 +++++++++++++++++----- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 9b80149176..cb2918cd9d 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1211,7 +1211,7 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix) { - // reference: http://www.gregslabaugh.net/publications/euler.pdf + // reference: http://eecs.qmul.ac.uk/~gslabaugh/publications/euler.pdf Vec3d angles1 = Vec3d::Zero(); Vec3d angles2 = Vec3d::Zero(); if (std::abs(std::abs(rotation_matrix(2, 0)) - 1.0) < 1e-5) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d8cdb515b3..fc5bc1c7ba 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -82,6 +82,8 @@ #define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4) // Enable editing volumes transformation in world coordinates and instances in local coordinates #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4) +// Enable showing world coordinates of volumes' offset relative to the instance containing them +#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (1 && ENABLE_WORLD_COORDINATE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index cf3d962453..be036714d4 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -620,13 +620,17 @@ void ObjectManipulation::update_settings_value(const Selection& selection) if (m_world_coordinates) { const Geometry::Transformation trafo(volume->world_matrix()); +#if ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET + const Vec3d offset = trafo.get_offset() - volume->get_instance_offset(); +#else const Vec3d& offset = trafo.get_offset(); const Vec3d& rotation = trafo.get_rotation(); +#endif // ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET const Vec3d& scaling_factor = trafo.get_scaling_factor(); // const Vec3d& mirror = trafo.get_mirror(); m_new_position = offset; - m_new_rotation = rotation * (180.0 / M_PI); + m_new_rotation = Vec3d::Zero(); m_new_scale = scaling_factor * 100.0; m_new_size = volume->bounding_box().size().cwiseProduct(scaling_factor); } @@ -774,7 +778,7 @@ void ObjectManipulation::update_reset_buttons_visibility() show_rotation = !rotation.isApprox(Vec3d::Zero()); show_scale = !scale.isApprox(Vec3d::Ones()); #if ENABLE_WORLD_COORDINATE - show_drop_to_bed = min_z < SINKING_Z_THRESHOLD; + show_drop_to_bed = std::abs(min_z) > EPSILON; #else show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; #endif // ENABLE_WORLD_COORDINATE @@ -920,6 +924,16 @@ void ObjectManipulation::change_rotation_value(int axis, double value) Selection& selection = canvas->get_selection(); TransformationType transformation_type(TransformationType::World_Relative_Joint); +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_full_instance()) + transformation_type.set_independent(); + + if (!m_world_coordinates) { + //FIXME Selection::rotate() does not process absolute rotations correctly: It does not recognize the axis index, which was changed. + // transformation_type.set_absolute(); + transformation_type.set_local(); + } +#else if (selection.is_single_full_instance() || selection.requires_local_axes()) transformation_type.set_independent(); if (selection.is_single_full_instance() && ! m_world_coordinates) { @@ -927,6 +941,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value) // transformation_type.set_absolute(); transformation_type.set_local(); } +#endif // ENABLE_WORLD_COORDINATE selection.start_dragging(); selection.rotate( diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d86ea5c137..dcdd062f67 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -827,13 +827,25 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ if (is_single_full_instance()) rotate_instance(v, i); else if (is_single_volume() || is_single_modifier()) { - if (transformation_type.independent()) - v.set_volume_rotation(v.get_volume_rotation() + rotation); +#if ENABLE_WORLD_COORDINATE + if (transformation_type.local()) + v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation); else { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - v.set_volume_rotation(new_rotation); + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + m = m * m_cache.volumes_data[i].get_instance_rotation_matrix(); + m = m * m_cache.volumes_data[i].get_volume_rotation_matrix(); + m = m_cache.volumes_data[i].get_instance_rotation_matrix().inverse() * m; + v.set_volume_rotation(Geometry::extract_euler_angles(m)); } +#else + if (transformation_type.independent()) + v.set_volume_rotation(v.get_volume_rotation() + rotation); + else { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + v.set_volume_rotation(new_rotation); + } +#endif // ENABLE_WORLD_COORDINATE } else { if (m_mode == Instance) From 8cf66f52f6043a97b1620bce75b18b0e106b0a17 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 18 Oct 2021 15:26:31 +0200 Subject: [PATCH 16/50] Tech ENABLE_WORLD_COORDINATE - Modified method Selection::requires_uniform_scale() --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 19 +++++++++---------- src/slic3r/GUI/Selection.cpp | 7 +++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index de7bdbcb6e..4e761c6b45 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -241,9 +241,12 @@ void GLGizmosManager::update_data() enable_grabber(Rotate, 0, !is_wipe_tower); enable_grabber(Rotate, 1, !is_wipe_tower); +#if ENABLE_WORLD_COORDINATE + bool enable_scale_xyz = !selection.requires_uniform_scale(); +#else bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier(); - for (unsigned int i = 0; i < 6; ++i) - { +#endif // ENABLE_WORLD_COORDINATE + for (unsigned int i = 0; i < 6; ++i) { enable_grabber(Scale, i, enable_scale_xyz); } @@ -252,8 +255,7 @@ void GLGizmosManager::update_data() ? get_current()->get_requirements() : CommonGizmosDataID(0)); - if (selection.is_single_full_instance()) - { + if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); set_scale(volume->get_instance_scaling_factor()); @@ -263,8 +265,7 @@ void GLGizmosManager::update_data() set_sla_support_data(model_object); set_painter_gizmo_data(); } - else if (selection.is_single_volume() || selection.is_single_modifier()) - { + else if (selection.is_single_volume() || selection.is_single_modifier()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); set_scale(volume->get_volume_scaling_factor()); set_rotation(Vec3d::Zero()); @@ -272,8 +273,7 @@ void GLGizmosManager::update_data() set_sla_support_data(nullptr); set_painter_gizmo_data(); } - else if (is_wipe_tower) - { + else if (is_wipe_tower) { DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; set_scale(Vec3d::Ones()); set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(config.option("wipe_tower_rotation_angle"))->value)); @@ -281,8 +281,7 @@ void GLGizmosManager::update_data() set_sla_support_data(nullptr); set_painter_gizmo_data(); } - else - { + else { set_scale(Vec3d::Ones()); set_rotation(Vec3d::Zero()); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index dcdd062f67..ae960349df 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -600,8 +600,15 @@ bool Selection::matches(const std::vector& volume_idxs) const bool Selection::requires_uniform_scale() const { +#if ENABLE_WORLD_COORDINATE + if (is_single_modifier() || is_single_volume()) + return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); + else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) + return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); +#else if (is_single_full_instance() || is_single_modifier() || is_single_volume()) return false; +#endif // ENABLE_WORLD_COORDINATE return true; } From 90874c19534c6b01202398f41c95653549663424 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 19 Oct 2021 10:04:22 +0200 Subject: [PATCH 17/50] Tech ENABLE_WORLD_COORDINATE - Fixed translation in local coordinate for single instance selection --- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 10 +++++++--- src/slic3r/GUI/Selection.cpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index f586d312d8..d19168822e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -76,13 +76,17 @@ void GLGizmoMove3D::on_start_dragging() #if ENABLE_WORLD_COORDINATE const BoundingBoxf3 box = get_selection_box(); Vec3d center; - if (wxGetApp().obj_manipul()->get_world_coordinates()) + if (wxGetApp().obj_manipul()->get_world_coordinates()) { center = box.center(); + m_starting_drag_position = center + m_grabbers[m_hover_id].center; + } else { const Selection& selection = m_parent.get_selection(); - center = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix() * box.center(); + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + const Transform3d trafo = Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()); + center = v.get_instance_offset() + trafo * box.center(); + m_starting_drag_position = center + trafo * m_grabbers[m_hover_id].center; } - m_starting_drag_position = center + m_grabbers[m_hover_id].center; m_starting_box_center = center; m_starting_box_bottom_center = center; m_starting_box_bottom_center.z() = box.min.z(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index ae960349df..e6181e0e90 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -728,7 +728,7 @@ void Selection::translate(const Vec3d& displacement, bool local) if (is_from_fully_selected_instance(i)) { if (local) { const VolumeCache& volume_data = m_cache.volumes_data[i]; - const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; + const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; v.set_instance_offset(volume_data.get_instance_position() + world_displacement); } else From bc93e4d626aed4f05affee022b2f04f40f41c808 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 19 Oct 2021 10:26:51 +0200 Subject: [PATCH 18/50] Tech ENABLE_WORLD_COORDINATE - Fixed rotation in local coordinate for single instance selection --- src/slic3r/GUI/Selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e6181e0e90..5b1adf3958 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -809,7 +809,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ const Vec3d new_rotation = transformation_type.world() ? Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()) : transformation_type.absolute() ? rotation : Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m); - if (rot_axis_max == 2 && transformation_type.world() && transformation_type.joint()) { + if (rot_axis_max == 2 && transformation_type.joint()) { // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); From d78db60b156258826dcd7361d7bc2a5bc52609f5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 Oct 2021 12:29:27 +0200 Subject: [PATCH 19/50] Partial revert of 7e5c214b91ae14740fc188413948818a1b49928a to restore code mistakenly removed and needed when tech ENABLE_WORLD_COORDINATE is disabled --- src/libslic3r/Technologies.hpp | 8 ++++---- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 11 ++++++++--- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6549ac8c97..4564d975d2 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -80,14 +80,14 @@ // Enable rendering modifiers and similar objects always as transparent #define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_ALPHA4) +// Enable the fix for the detection of the out of bed state for sinking objects +// and detection of out of bed using the bed perimeter +#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4) + // Enable editing volumes transformation in world coordinates and instances in local coordinates #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4) // Enable showing world coordinates of volumes' offset relative to the instance containing them #define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (1 && ENABLE_WORLD_COORDINATE) -// Enable the fix for the detection of the out of bed state for sinking objects -// and detection of out of bed using the bed perimeter -#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4) - #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 8fc2e852c6..dc0656c49d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -145,6 +145,7 @@ void GLGizmoScale3D::on_render() #else // Transforms grabbers' offsets to world refefence system Transform3d offsets_transform = Transform3d::Identity(); + m_offsets_transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); if (selection.is_single_full_instance()) { @@ -171,6 +172,7 @@ void GLGizmoScale3D::on_render() angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); + m_offsets_transform = offsets_transform; #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE @@ -191,6 +193,7 @@ void GLGizmoScale3D::on_render() angles = Geometry::extract_euler_angles(m_transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); + m_offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), v->get_volume_rotation(), Vec3d::Ones(), v->get_volume_mirror()); #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE @@ -421,9 +424,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) const double inner_ratio = len_center_vec / len_starting_vec; double local_offset = inner_ratio * 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); #else - double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); - if (!m_parent.get_selection().is_single_full_instance()) - local_offset *= m_starting.scale(axis); + double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis); #endif // ENABLE_WORLD_COORDINATE if (m_hover_id == 2 * axis) @@ -438,7 +439,11 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: break; } +#if ENABLE_WORLD_COORDINATE m_offset = local_offset_vec; +#else + m_offset = m_offsets_transform * local_offset_vec; +#endif // ENABLE_WORLD_COORDINATE } else m_offset = Vec3d::Zero(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 639e4a6cb3..26badc5252 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -29,6 +29,9 @@ class GLGizmoScale3D : public GLGizmoBase #if ENABLE_WORLD_COORDINATE Transform3d m_grabbers_transform; Vec3d m_center{ Vec3d::Zero() }; +#else + // Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes) + Transform3d m_offsets_transform; #endif // ENABLE_WORLD_COORDINATE Vec3d m_scale{ Vec3d::Ones() }; Vec3d m_offset{ Vec3d::Zero() }; From b5ba6590e15296cd50bd60d654c8b681b04394bc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 08:23:13 +0200 Subject: [PATCH 20/50] Tech ENABLE_WORLD_COORDINATE - Fixed constrained non-uniform scaling in world coordinates for rotated instances --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 19 ++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 ++++++ src/slic3r/GUI/Selection.cpp | 10 +++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index dc0656c49d..fde0e0e5ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -412,11 +412,28 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) { #if ENABLE_WORLD_COORDINATE double ratio = calc_ratio(data); + if (ratio > 0.0) { + Vec3d curr_scale = m_scale; + Vec3d starting_scale = m_starting.scale; + const Selection& selection = m_parent.get_selection(); + const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); + if (selection.is_single_full_instance() && world_coordinates) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); + curr_scale = (m * curr_scale).cwiseAbs(); + starting_scale = (m * starting_scale).cwiseAbs(); + } + + curr_scale(axis) = starting_scale(axis) * ratio; + + if (selection.is_single_full_instance() && world_coordinates) + m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); + else + m_scale = curr_scale; #else const double ratio = calc_ratio(data); -#endif // ENABLE_WORLD_COORDINATE if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; +#endif // ENABLE_WORLD_COORDINATE if (m_starting.ctrl_down) { #if ENABLE_WORLD_COORDINATE const double len_starting_vec = std::abs(m_starting.box.center()(axis) - m_starting.pivots[m_hover_id](axis)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index a57abe30f4..0c7827653b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -627,7 +627,13 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Scale: { // Apply new temporary scale factors +#if ENABLE_WORLD_COORDINATE + TransformationType transformation_type; + if (!wxGetApp().obj_manipul()->get_world_coordinates()) + transformation_type.set_local(); +#else TransformationType transformation_type(TransformationType::Local_Absolute_Joint); +#endif // ENABLE_WORLD_COORDINATE if (evt.AltDown()) transformation_type.set_independent(); selection.scale(get_scale(), transformation_type); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 6d8ad6bc72..82ccb46f22 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -960,16 +960,19 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type GLVolume &v = *(*m_volumes)[i]; if (is_single_full_instance()) { if (transformation_type.relative()) { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); + const Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + const Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); if (transformation_type.joint()) v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); v.set_instance_scaling_factor(new_scale); } else { +#if ENABLE_WORLD_COORDINATE + v.set_instance_scaling_factor(scale); +#else if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { // Non-uniform scaling. Transform the scaling factors into the local coordinate system. // This is only possible, if the instance rotation is mulitples of ninety degrees. @@ -978,6 +981,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type } else v.set_instance_scaling_factor(scale); +#endif // ENABLE_WORLD_COORDINATE } } else if (is_single_volume() || is_single_modifier()) From 86b44b48005e3ddb96e851e19f8e2ccd1a38b667 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 09:32:49 +0200 Subject: [PATCH 21/50] Tech ENABLE_WORLD_COORDINATE - Added constrained uniform scaling in world coordinates for instances --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 23 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 3 ++- src/slic3r/GUI/Selection.cpp | 4 +++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 02ca898525..4f85667e0d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1095,6 +1095,7 @@ void ObjectManipulation::set_world_coordinates(const bool world_coordinates) m_world_coordinates = world_coordinates; this->UpdateAndShow(true); GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->get_gizmos_manager().update_data(); canvas->set_as_dirty(); canvas->request_extra_frame(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index fde0e0e5ea..deaa95b904 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -101,6 +101,10 @@ void GLGizmoScale3D::on_start_dragging() m_starting.pivots[3] = trafo * Vec3d(center.x(), m_starting.box.min.y(), center.z()); m_starting.pivots[4] = trafo * Vec3d(center.x(), center.y(), m_starting.box.max.z()); m_starting.pivots[5] = trafo * Vec3d(center.x(), center.y(), m_starting.box.min.z()); + m_starting.pivots[6] = trafo * Vec3d(m_starting.box.max.x(), m_starting.box.max.y(), center.z()); + m_starting.pivots[7] = trafo * Vec3d(m_starting.box.min.x(), m_starting.box.max.y(), center.z()); + m_starting.pivots[8] = trafo * Vec3d(m_starting.box.min.x(), m_starting.box.min.y(), center.z()); + m_starting.pivots[9] = trafo * Vec3d(m_starting.box.max.x(), m_starting.box.min.y(), center.z()); } #else m_starting.drag_position = m_grabbers[m_hover_id].center; @@ -261,18 +265,22 @@ void GLGizmoScale3D::on_render() // uniform #if ENABLE_WORLD_COORDINATE m_grabbers[6].center = { -(box_half_size.x() + Offset), -(box_half_size.y() + Offset), 0.0 }; + m_grabbers[6].color = (use_constrain && m_hover_id == 8) ? CONSTRAINED_COLOR : m_highlight_color; m_grabbers[7].center = { box_half_size.x() + Offset, -(box_half_size.y() + Offset), 0.0 }; + m_grabbers[7].color = (use_constrain && m_hover_id == 9) ? CONSTRAINED_COLOR : m_highlight_color; m_grabbers[8].center = { box_half_size.x() + Offset, box_half_size.y() + Offset, 0.0 }; + m_grabbers[8].color = (use_constrain && m_hover_id == 6) ? CONSTRAINED_COLOR : m_highlight_color; m_grabbers[9].center = { -(box_half_size.x() + Offset), box_half_size.y() + Offset, 0.0 }; + m_grabbers[9].color = (use_constrain && m_hover_id == 7) ? CONSTRAINED_COLOR : m_highlight_color; #else m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; -#endif // ENABLE_WORLD_COORDINATE for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } +#endif // ENABLE_WORLD_COORDINATE #if !ENABLE_WORLD_COORDINATE // sets grabbers orientation @@ -472,7 +480,20 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) const double ratio = calc_ratio(data); if (ratio > 0.0) { m_scale = m_starting.scale * ratio; +#if ENABLE_WORLD_COORDINATE + if (m_starting.ctrl_down) { + m_offset = 0.5 * (ratio - 1.0) * m_starting.box.size(); + if (m_hover_id == 6 || m_hover_id == 9) + m_offset.x() *= -1.0; + if (m_hover_id == 6 || m_hover_id == 7) + m_offset.y() *= -1.0; + } + else { +#endif // ENABLE_WORLD_COORDINATE m_offset = Vec3d::Zero(); +#if ENABLE_WORLD_COORDINATE + } +#endif // ENABLE_WORLD_COORDINATE } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 26badc5252..160fd4b179 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -21,7 +21,8 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d center{ Vec3d::Zero() }; #endif // ENABLE_WORLD_COORDINATE BoundingBoxf3 box; - std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), + Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(),Vec3d::Zero() }; }; BoundingBoxf3 m_box; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 82ccb46f22..ffc63f2883 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -608,12 +608,14 @@ bool Selection::requires_uniform_scale() const return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); + + return false; #else if (is_single_full_instance() || is_single_modifier() || is_single_volume()) return false; -#endif // ENABLE_WORLD_COORDINATE return true; +#endif // ENABLE_WORLD_COORDINATE } int Selection::get_object_idx() const From 7c6ce0ec7a85caef67c12327c61c8286f8866adf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 11:27:36 +0200 Subject: [PATCH 22/50] Follow-up of 86b44b48005e3ddb96e851e19f8e2ccd1a38b667 - Constrained uniform scaling in world coordinates for rotated instances --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 7 +++---- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index deaa95b904..c456a6fe9f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -101,10 +101,6 @@ void GLGizmoScale3D::on_start_dragging() m_starting.pivots[3] = trafo * Vec3d(center.x(), m_starting.box.min.y(), center.z()); m_starting.pivots[4] = trafo * Vec3d(center.x(), center.y(), m_starting.box.max.z()); m_starting.pivots[5] = trafo * Vec3d(center.x(), center.y(), m_starting.box.min.z()); - m_starting.pivots[6] = trafo * Vec3d(m_starting.box.max.x(), m_starting.box.max.y(), center.z()); - m_starting.pivots[7] = trafo * Vec3d(m_starting.box.min.x(), m_starting.box.max.y(), center.z()); - m_starting.pivots[8] = trafo * Vec3d(m_starting.box.min.x(), m_starting.box.min.y(), center.z()); - m_starting.pivots[9] = trafo * Vec3d(m_starting.box.max.x(), m_starting.box.min.y(), center.z()); } #else m_starting.drag_position = m_grabbers[m_hover_id].center; @@ -483,10 +479,13 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) #if ENABLE_WORLD_COORDINATE if (m_starting.ctrl_down) { m_offset = 0.5 * (ratio - 1.0) * m_starting.box.size(); + if (m_hover_id == 6 || m_hover_id == 9) m_offset.x() *= -1.0; if (m_hover_id == 6 || m_hover_id == 7) m_offset.y() *= -1.0; + + m_offset += (ratio - 1.0) * (m_starting.center - m_starting.box.center()); } else { #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 160fd4b179..26badc5252 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -21,8 +21,7 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d center{ Vec3d::Zero() }; #endif // ENABLE_WORLD_COORDINATE BoundingBoxf3 box; - std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), - Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(),Vec3d::Zero() }; + std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; }; BoundingBoxf3 m_box; From a31594b5627eacf824e819be225794a6f5988ef2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 12:16:59 +0200 Subject: [PATCH 23/50] Tech ENABLE_WORLD_COORDINATE - Fixed unconstrained scaling in world coordinates for volumes --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 ++++++ src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 12 ++++++++++++ src/slic3r/GUI/Selection.cpp | 12 ++++++------ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 4f85667e0d..e013e5f2d8 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1018,12 +1018,18 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); Vec3d scaling_factor = scale; +#if ENABLE_WORLD_COORDINATE + TransformationType transformation_type; + if (!m_world_coordinates) + transformation_type.set_local(); +#else TransformationType transformation_type(TransformationType::World_Relative_Joint); if (selection.is_single_full_instance()) { transformation_type.set_absolute(); if (! m_world_coordinates) transformation_type.set_local(); } +#endif // ENABLE_WORLD_COORDINATE if (m_uniform_scale || selection.requires_uniform_scale()) scaling_factor = scale(axis) * Vec3d::Ones(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index c456a6fe9f..279190c937 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -426,11 +426,23 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) curr_scale = (m * curr_scale).cwiseAbs(); starting_scale = (m * starting_scale).cwiseAbs(); } + else if ((selection.is_single_volume() || selection.is_single_modifier()) && world_coordinates) { + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + const Transform3d m = mi * mv; + curr_scale = (m * curr_scale).cwiseAbs(); + starting_scale = (m * starting_scale).cwiseAbs(); + } curr_scale(axis) = starting_scale(axis) * ratio; if (selection.is_single_full_instance() && world_coordinates) m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); + else if ((selection.is_single_volume() || selection.is_single_modifier()) && world_coordinates) { + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); + m_scale = (mv * mi * curr_scale).cwiseAbs(); + } else m_scale = curr_scale; #else diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index ffc63f2883..e07c2c95b9 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -989,22 +989,22 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type else if (is_single_volume() || is_single_modifier()) v.set_volume_scaling_factor(scale); else { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); if (m_mode == Instance) { - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + const Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + const Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); if (transformation_type.joint()) v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); v.set_instance_scaling_factor(new_scale); } else if (m_mode == Volume) { - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); + const Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + const Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); if (transformation_type.joint()) { - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); + const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); } v.set_volume_scaling_factor(new_scale); From 47994f33ba7b2ce23c23550e52d10120f2b36c00 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 14:05:56 +0200 Subject: [PATCH 24/50] Tech ENABLE_WORLD_COORDINATE - Fixed scaling using the Part Manipulation fields in world coordinates for volumes --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 22 ++++++++++++++++++---- src/slic3r/GUI/Selection.cpp | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index e013e5f2d8..f76fa4ff99 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -626,13 +626,12 @@ void ObjectManipulation::update_settings_value(const Selection& selection) const Vec3d& offset = trafo.get_offset(); const Vec3d& rotation = trafo.get_rotation(); #endif // ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET - const Vec3d& scaling_factor = trafo.get_scaling_factor(); // const Vec3d& mirror = trafo.get_mirror(); m_new_position = offset; m_new_rotation = Vec3d::Zero(); - m_new_scale = scaling_factor * 100.0; - m_new_size = volume->bounding_box().size().cwiseProduct(scaling_factor); + m_new_size = volume->transformed_convex_hull_bounding_box(trafo.get_matrix()).size(); + m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_instance_transformation().get_matrix() * volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0; } else { #endif // ENABLE_WORLD_COORDINATE @@ -1016,12 +1015,27 @@ void ObjectManipulation::change_size_value(int axis, double value) void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const { Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); +#if !ENABLE_WORLD_COORDINATE Vec3d scaling_factor = scale; +#endif // !ENABLE_WORLD_COORDINATE #if ENABLE_WORLD_COORDINATE TransformationType transformation_type; if (!m_world_coordinates) transformation_type.set_local(); + + bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale(); + Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale; + + if (!uniform_scale && m_world_coordinates) { + if (selection.is_single_full_instance()) + scaling_factor = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * scaling_factor).cwiseAbs(); + else if (selection.is_single_volume() || selection.is_single_modifier()) { + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); + scaling_factor = (mv * mi * scaling_factor).cwiseAbs(); + } + } #else TransformationType transformation_type(TransformationType::World_Relative_Joint); if (selection.is_single_full_instance()) { @@ -1029,10 +1043,10 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const if (! m_world_coordinates) transformation_type.set_local(); } -#endif // ENABLE_WORLD_COORDINATE if (m_uniform_scale || selection.requires_uniform_scale()) scaling_factor = scale(axis) * Vec3d::Ones(); +#endif // ENABLE_WORLD_COORDINATE selection.start_dragging(); selection.scale(scaling_factor, transformation_type); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e07c2c95b9..dbed85e86a 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -609,7 +609,7 @@ bool Selection::requires_uniform_scale() const else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); - return false; + return true; #else if (is_single_full_instance() || is_single_modifier() || is_single_volume()) return false; @@ -688,7 +688,7 @@ const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const const GLVolume& volume = *(*m_volumes)[i]; if (volume.is_modifier) continue; - Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix(); + Transform3d trafo = volume.get_instance_transformation().get_matrix() * volume.get_volume_transformation().get_matrix(); trafo.translation().z() += volume.get_sla_shift_z(); (*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo)); } From 9ddf2ba41ccc59650c277d5a5b4604afa8702f3b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 Oct 2021 11:39:29 +0200 Subject: [PATCH 25/50] Tech ENABLE_WORLD_COORDINATE - Fixed constrained scaling of instances in local coordinates --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 12 ++-- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 86 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 3 + src/slic3r/GUI/Selection.cpp | 12 +++- 4 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index f76fa4ff99..52e9581163 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -765,11 +765,15 @@ void ObjectManipulation::update_reset_buttons_visibility() #endif // ENABLE_WORLD_COORDINATE double min_z = 0.0; -#if ENABLE_WORLD_COORDINATE - if (selection.is_single_full_instance() && m_world_coordinates) { -#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#if ENABLE_WORLD_COORDINATE +// if (selection.is_single_full_instance() && m_world_coordinates) { +//#else +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (selection.is_single_full_instance()) { -#endif // ENABLE_WORLD_COORDINATE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#endif // ENABLE_WORLD_COORDINATE +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); min_z = selection.get_scaled_instance_bounding_box().min.z(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 279190c937..d3771b57cf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -91,17 +91,7 @@ void GLGizmoScale3D::on_start_dragging() m_starting.drag_position = m_grabbers_transform * m_grabbers[m_hover_id].center; m_starting.box = m_box; m_starting.center = m_center; - - if (m_starting.ctrl_down) { - const Vec3d center = m_starting.box.center(); - const Transform3d trafo = wxGetApp().obj_manipul()->get_world_coordinates() ? Transform3d::Identity() : m_transform; - m_starting.pivots[0] = trafo * Vec3d(m_starting.box.max.x(), center.y(), center.z()); - m_starting.pivots[1] = trafo * Vec3d(m_starting.box.min.x(), center.y(), center.z()); - m_starting.pivots[2] = trafo * Vec3d(center.x(), m_starting.box.max.y(), center.z()); - m_starting.pivots[3] = trafo * Vec3d(center.x(), m_starting.box.min.y(), center.z()); - m_starting.pivots[4] = trafo * Vec3d(center.x(), center.y(), m_starting.box.max.z()); - m_starting.pivots[5] = trafo * Vec3d(center.x(), center.y(), m_starting.box.min.z()); - } + m_starting.transform = wxGetApp().obj_manipul()->get_world_coordinates() ? Transform3d::Identity() : m_transform; #else m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_box : m_parent.get_selection().get_bounding_box(); @@ -140,8 +130,8 @@ void GLGizmoScale3D::on_render() m_transform = Transform3d::Identity(); #if ENABLE_WORLD_COORDINATE m_grabbers_transform = Transform3d::Identity(); - - if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) { + bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); + if (selection.is_single_full_instance() && !world_coordinates) { #else // Transforms grabbers' offsets to world refefence system Transform3d offsets_transform = Transform3d::Identity(); @@ -149,25 +139,31 @@ void GLGizmoScale3D::on_render() Vec3d angles = Vec3d::Zero(); if (selection.is_single_full_instance()) { -#endif // !ENABLE_WORLD_COORDINATE - +#endif // ENABLE_WORLD_COORDINATE // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { const GLVolume* v = selection.get_volume(idx); #if ENABLE_WORLD_COORDINATE - m_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); + m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); #else m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); #endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_WORLD_COORDINATE + m_box = m_box.transformed(selection.get_volume(*idxs.begin())->get_instance_transformation().get_matrix(true, true, false, true)); +#endif // ENABLE_WORLD_COORDINATE + // gets transform from first selected volume const GLVolume* v = selection.get_volume(*idxs.begin()); - m_transform = v->get_instance_transformation().get_matrix(); #if ENABLE_WORLD_COORDINATE - m_grabbers_transform = v->get_instance_transformation().get_matrix(true, true, false, true); + m_transform = v->get_instance_transformation().get_matrix(false, false, true); + m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * Geometry::assemble_transform(m_box.center()); + m_center = v->get_instance_offset(); #else + m_transform = v->get_instance_transformation().get_matrix(); + // gets angles from first selected volume angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets @@ -176,7 +172,7 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE - else if ((selection.is_single_modifier() || selection.is_single_volume()) && !wxGetApp().obj_manipul()->get_world_coordinates()) { + else if ((selection.is_single_modifier() || selection.is_single_volume()) && !world_coordinates) { #else else if (selection.is_single_modifier() || selection.is_single_volume()) { #endif // ENABLE_WORLD_COORDINATE @@ -210,14 +206,14 @@ void GLGizmoScale3D::on_render() Vec3d offset_x = offsets_transform * (Offset * Vec3d::UnitX()); Vec3d offset_y = offsets_transform * (Offset * Vec3d::UnitY()); Vec3d offset_z = offsets_transform * (Offset * Vec3d::UnitZ()); -#endif // ENABLE_WORLD_COORDINATE bool ctrl_down = m_dragging && m_starting.ctrl_down || !m_dragging && wxGetKeyState(WXK_CONTROL); +#endif // ENABLE_WORLD_COORDINATE // x axis #if ENABLE_WORLD_COORDINATE const Vec3d box_half_size = 0.5 * m_box.size(); - bool use_constrain = ctrl_down && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); + bool use_constrain = wxGetKeyState(WXK_CONTROL) && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); m_grabbers[0].center = { -(box_half_size.x() + Offset), 0.0, 0.0 }; m_grabbers[0].color = (use_constrain && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; @@ -452,10 +448,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) #endif // ENABLE_WORLD_COORDINATE if (m_starting.ctrl_down) { #if ENABLE_WORLD_COORDINATE - const double len_starting_vec = std::abs(m_starting.box.center()(axis) - m_starting.pivots[m_hover_id](axis)); - const double len_center_vec = std::abs(m_starting.center(axis) - m_starting.pivots[m_hover_id](axis)); - const double inner_ratio = len_center_vec / len_starting_vec; - double local_offset = inner_ratio * 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); + double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); #else double local_offset = 0.5 * (m_scale(axis) - m_starting.scale(axis)) * m_starting.box.size()(axis); #endif // ENABLE_WORLD_COORDINATE @@ -463,6 +456,23 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (m_hover_id == 2 * axis) local_offset *= -1.0; +#if ENABLE_WORLD_COORDINATE + Vec3d center_offset = m_starting.center - m_starting.transform * m_starting.box.center(); + if (selection.is_single_full_instance() && !world_coordinates) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); + center_offset = m * center_offset; + } + + local_offset += (ratio - 1.0) * center_offset(axis); + + switch (axis) + { + case X: { m_offset = local_offset * Vec3d::UnitX(); break; } + case Y: { m_offset = local_offset * Vec3d::UnitY(); break; } + case Z: { m_offset = local_offset * Vec3d::UnitZ(); break; } + default: { m_offset = Vec3d::Zero(); break; } + } +#else Vec3d local_offset_vec; switch (axis) { @@ -472,9 +482,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: break; } -#if ENABLE_WORLD_COORDINATE - m_offset = local_offset_vec; -#else m_offset = m_offsets_transform * local_offset_vec; #endif // ENABLE_WORLD_COORDINATE } @@ -497,24 +504,33 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) if (m_hover_id == 6 || m_hover_id == 7) m_offset.y() *= -1.0; - m_offset += (ratio - 1.0) * (m_starting.center - m_starting.box.center()); + const Selection& selection = m_parent.get_selection(); + const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); + Vec3d center_offset = m_starting.center - m_starting.transform * m_starting.box.center(); + if (selection.is_single_full_instance() && !world_coordinates) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); + center_offset = m * center_offset; + } + + m_offset += (ratio - 1.0) * center_offset; } - else { + else #endif // ENABLE_WORLD_COORDINATE m_offset = Vec3d::Zero(); -#if ENABLE_WORLD_COORDINATE - } -#endif // ENABLE_WORLD_COORDINATE - } +} } double GLGizmoScale3D::calc_ratio(const UpdateData& data) const { double ratio = 0.0; +#if ENABLE_WORLD_COORDINATE + const Vec3d starting_vec = m_starting.drag_position - m_starting.center; +#else const Vec3d pivot = (m_starting.ctrl_down && m_hover_id < 6) ? m_starting.pivots[m_hover_id] : m_starting.box.center(); - const Vec3d starting_vec = m_starting.drag_position - pivot; +#endif // ENABLE_WORLD_COORDINATE + const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 26badc5252..c011e4bff2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -19,9 +19,12 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d drag_position{ Vec3d::Zero() }; #if ENABLE_WORLD_COORDINATE Vec3d center{ Vec3d::Zero() }; + Transform3d transform; #endif // ENABLE_WORLD_COORDINATE BoundingBoxf3 box; +#if !ENABLE_WORLD_COORDINATE std::array pivots{ Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; +#endif // !ENABLE_WORLD_COORDINATE }; BoundingBoxf3 m_box; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index dbed85e86a..221f3f6148 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -606,8 +606,16 @@ bool Selection::requires_uniform_scale() const #if ENABLE_WORLD_COORDINATE if (is_single_modifier() || is_single_volume()) return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); - else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) - return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (is_single_full_instance()) { + if (wxGetApp().obj_manipul()->get_world_coordinates()) + return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); + else + return false; + } +// else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) +// return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return true; #else From 4442e79fa680bba8c0732a4372606f89f54d9dc3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 Oct 2021 11:55:11 +0200 Subject: [PATCH 26/50] Follow-up of 9ddf2ba41ccc59650c277d5a5b4604afa8702f3b - Code cleanup --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 8 -------- src/slic3r/GUI/Selection.cpp | 13 +++---------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 52e9581163..6986ae355f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -765,15 +765,7 @@ void ObjectManipulation::update_reset_buttons_visibility() #endif // ENABLE_WORLD_COORDINATE double min_z = 0.0; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#if ENABLE_WORLD_COORDINATE -// if (selection.is_single_full_instance() && m_world_coordinates) { -//#else -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (selection.is_single_full_instance()) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#endif // ENABLE_WORLD_COORDINATE -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ rotation = volume->get_instance_rotation(); scale = volume->get_instance_scaling_factor(); min_z = selection.get_scaled_instance_bounding_box().min.z(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 221f3f6148..5e23f5d203 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -606,16 +606,9 @@ bool Selection::requires_uniform_scale() const #if ENABLE_WORLD_COORDINATE if (is_single_modifier() || is_single_volume()) return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - else if (is_single_full_instance()) { - if (wxGetApp().obj_manipul()->get_world_coordinates()) - return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); - else - return false; - } -// else if (is_single_full_instance() && wxGetApp().obj_manipul()->get_world_coordinates()) -// return !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (is_single_full_instance()) + return wxGetApp().obj_manipul()->get_world_coordinates() ? + !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()) : false; return true; #else From 7ec4957269afade27c17ea7b6d5ab9d7bfa474c0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 Oct 2021 13:58:53 +0200 Subject: [PATCH 27/50] Tech ENABLE_WORLD_COORDINATE - Fixed unconstrained scaling of volumes in local coordinates --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 4 ++++ src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 12 ++++++++---- src/slic3r/GUI/Selection.cpp | 8 +++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6986ae355f..b015094b44 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -709,7 +709,11 @@ void ObjectManipulation::update_if_dirty() if (selection.requires_uniform_scale()) { m_lock_bnt->SetLock(true); +#if ENABLE_WORLD_COORDINATE + m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection or non axis-aligned objects/parts")); +#else m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection")); +#endif // ENABLE_WORLD_COORDINATE m_lock_bnt->disable(); } else { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index d3771b57cf..e2a44ce082 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -178,14 +178,16 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_WORLD_COORDINATE const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_WORLD_COORDINATE - m_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); + m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix(true, true, false, true))); #else m_box = v->bounding_box(); #endif // ENABLE_WORLD_COORDINATE - m_transform = v->world_matrix(); #if ENABLE_WORLD_COORDINATE - m_grabbers_transform = m_transform; + m_transform = v->get_volume_transformation().get_matrix(false, false, true); + m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * m_transform * Geometry::assemble_transform(m_box.center()); + m_center = v->world_matrix() * m_box.center(); #else + m_transform = v->world_matrix(); angles = Geometry::extract_euler_angles(m_transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); @@ -562,7 +564,9 @@ void GLGizmoScale3D::transform_to_local(const Selection& selection) const glsafe(::glTranslated(center.x(), center.y(), center.z())); if (!wxGetApp().obj_manipul()->get_world_coordinates()) { - const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + if (selection.is_single_volume() || selection.is_single_modifier()) + orient_matrix = orient_matrix * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 5e23f5d203..a6215b5dbf 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1379,7 +1379,13 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const glsafe(::glTranslated(center.x(), center.y(), center.z())); #if ENABLE_WORLD_COORDINATE if (!wxGetApp().obj_manipul()->get_world_coordinates()) { - const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + Transform3d orient_matrix = Transform3d::Identity(); + if (boost::starts_with(sidebar_field, "scale")) { + const GLVolume* v = (*m_volumes)[*m_list.begin()]; + orient_matrix = v->get_instance_transformation().get_matrix(true, false, true, true) * v->get_volume_transformation().get_matrix(true, false, true, true); + } + else + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } #else From 977d530ccee0694bb69e04e17df4d1dd0f902798 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 26 Oct 2021 14:49:47 +0200 Subject: [PATCH 28/50] Tech ENABLE_WORLD_COORDINATE - Fixed constrained scaling of volumes in local coordinates --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index e2a44ce082..8cf0796f66 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -183,8 +183,8 @@ void GLGizmoScale3D::on_render() m_box = v->bounding_box(); #endif // ENABLE_WORLD_COORDINATE #if ENABLE_WORLD_COORDINATE - m_transform = v->get_volume_transformation().get_matrix(false, false, true); - m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * m_transform * Geometry::assemble_transform(m_box.center()); + m_transform = v->world_matrix(); + m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * v->get_volume_transformation().get_matrix(false, false, true); m_center = v->world_matrix() * m_box.center(); #else m_transform = v->world_matrix(); @@ -474,6 +474,11 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) case Z: { m_offset = local_offset * Vec3d::UnitZ(); break; } default: { m_offset = Vec3d::Zero(); break; } } + + if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + m_offset = m * m_offset; + } #else Vec3d local_offset_vec; switch (axis) @@ -515,6 +520,11 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) } m_offset += (ratio - 1.0) * center_offset; + + if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + m_offset = m * m_offset; + } } else #endif // ENABLE_WORLD_COORDINATE From d288cbfde51bc5c1a2a4cf82e9daa7f1795bb24a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 27 Oct 2021 13:29:50 +0200 Subject: [PATCH 29/50] Tech ENABLE_WORLD_COORDINATE - Fixed visualization of sidebar hints when editing values in Object manipulation fields while using an MMU printer --- src/slic3r/GUI/GUI_ObjectList.cpp | 6 ++++++ src/slic3r/GUI/GUI_ObjectManipulation.hpp | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 43ebd23f2b..49516daa4e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2542,7 +2542,13 @@ void ObjectList::part_selection_changed() Sidebar& panel = wxGetApp().sidebar(); panel.Freeze(); +#if ENABLE_WORLD_COORDINATE + const ManipulationEditor* const editor = wxGetApp().obj_manipul()->get_focused_editor(); + const std::string opt_key = (editor != nullptr) ? editor->get_full_opt_name() : ""; + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, !opt_key.empty()); +#else wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false); +#endif // ENABLE_WORLD_COORDINATE wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations); wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings); wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index a4f826feac..849a443f43 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -57,6 +57,10 @@ public: void set_value(const wxString& new_value); void kill_focus(ObjectManipulation *parent); +#if ENABLE_WORLD_COORDINATE + const std::string& get_full_opt_name() const { return m_full_opt_name; } +#endif // ENABLE_WORLD_COORDINATE + private: double get_value(); }; @@ -152,10 +156,15 @@ private: ScalableBitmap m_manifold_warning_bmp; wxStaticBitmap* m_fix_throught_netfab_bitmap; +#if ENABLE_WORLD_COORDINATE + // Currently focused editor (nullptr if none) + ManipulationEditor* m_focused_editor{ nullptr }; +#else #ifndef __APPLE__ // Currently focused editor (nullptr if none) ManipulationEditor* m_focused_editor {nullptr}; #endif // __APPLE__ +#endif // ENABLE_WORLD_COORDINATE wxFlexGridSizer* m_main_grid_sizer; wxFlexGridSizer* m_labels_grid_sizer; @@ -205,11 +214,19 @@ public: void sys_color_changed(); void on_change(const std::string& opt_key, int axis, double new_value); void set_focused_editor(ManipulationEditor* focused_editor) { +#if ENABLE_WORLD_COORDINATE + m_focused_editor = focused_editor; +#else #ifndef __APPLE__ m_focused_editor = focused_editor; #endif // __APPLE__ +#endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_WORLD_COORDINATE + ManipulationEditor* get_focused_editor() { return m_focused_editor; } +#endif // ENABLE_WORLD_COORDINATE + private: void reset_settings_value(); void update_settings_value(const Selection& selection); From d3faf9435cd902d3ce9f6825b4e20f9b431ca147 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Nov 2021 12:03:13 +0100 Subject: [PATCH 30/50] Tech ENABLE_WORLD_COORDINATE - Fixed center of Move and Scale gizmos --- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 69 ++++---- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 201 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 7 +- 5 files changed, 154 insertions(+), 134 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index d19168822e..1af59c8e0c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -74,22 +74,16 @@ void GLGizmoMove3D::on_start_dragging() if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); #if ENABLE_WORLD_COORDINATE - const BoundingBoxf3 box = get_selection_box(); - Vec3d center; - if (wxGetApp().obj_manipul()->get_world_coordinates()) { - center = box.center(); - m_starting_drag_position = center + m_grabbers[m_hover_id].center; - } + if (wxGetApp().obj_manipul()->get_world_coordinates()) + m_starting_drag_position = m_center + m_grabbers[m_hover_id].center; else { const Selection& selection = m_parent.get_selection(); const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); - const Transform3d trafo = Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()); - center = v.get_instance_offset() + trafo * box.center(); - m_starting_drag_position = center + trafo * m_grabbers[m_hover_id].center; + m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * m_grabbers[m_hover_id].center; } - m_starting_box_center = center; - m_starting_box_bottom_center = center; - m_starting_box_bottom_center.z() = box.min.z(); + m_starting_box_center = m_center; + m_starting_box_bottom_center = m_center; + m_starting_box_bottom_center.z() = m_bounding_box.min.z(); #else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_starting_drag_position = m_grabbers[m_hover_id].center; @@ -122,11 +116,11 @@ void GLGizmoMove3D::on_render() #if ENABLE_WORLD_COORDINATE glsafe(::glPushMatrix()); + calc_selection_box_and_center(); transform_to_local(m_parent.get_selection()); const Vec3d zero = Vec3d::Zero(); - BoundingBoxf3 box = get_selection_box(); - const Vec3d half_box_size = 0.5 * box.size(); + const Vec3d half_box_size = 0.5 * m_bounding_box.size(); // x axis m_grabbers[0].center = { half_box_size.x() + Offset, 0.0, 0.0 }; @@ -176,11 +170,19 @@ void GLGizmoMove3D::on_render() } // draw grabbers +#if ENABLE_WORLD_COORDINATE + render_grabbers(m_bounding_box); + for (unsigned int i = 0; i < 3; ++i) { + if (m_grabbers[i].enabled) + render_grabber_extension((Axis)i, m_bounding_box, false); + } +#else render_grabbers(box); for (unsigned int i = 0; i < 3; ++i) { if (m_grabbers[i].enabled) render_grabber_extension((Axis)i, box, false); } +#endif // ENABLE_WORLD_COORDINATE } else { // draw axis @@ -199,12 +201,20 @@ void GLGizmoMove3D::on_render() shader->start_using(); shader->set_uniform("emission_factor", 0.1f); // draw grabber +#if ENABLE_WORLD_COORDINATE + const Vec3d box_size = m_bounding_box.size(); +#else const Vec3d box_size = box.size(); +#endif // ENABLE_WORLD_COORDINATE const float mean_size = (float)((box_size.x() + box_size.y() + box_size.z()) / 3.0); m_grabbers[m_hover_id].render(true, mean_size); shader->stop_using(); } +#if ENABLE_WORLD_COORDINATE + render_grabber_extension((Axis)m_hover_id, m_bounding_box, false); +#else render_grabber_extension((Axis)m_hover_id, box, false); +#endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE @@ -219,18 +229,17 @@ void GLGizmoMove3D::on_render_for_picking() #if ENABLE_WORLD_COORDINATE glsafe(::glPushMatrix()); transform_to_local(m_parent.get_selection()); - const BoundingBoxf3 box = get_selection_box(); + render_grabbers_for_picking(m_bounding_box); + render_grabber_extension(X, m_bounding_box, true); + render_grabber_extension(Y, m_bounding_box, true); + render_grabber_extension(Z, m_bounding_box, true); + glsafe(::glPopMatrix()); #else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); -#endif // ENABLE_WORLD_COORDINATE - render_grabbers_for_picking(box); render_grabber_extension(X, box, true); render_grabber_extension(Y, box, true); render_grabber_extension(Z, box, true); - -#if ENABLE_WORLD_COORDINATE - glsafe(::glPopMatrix()); #endif // ENABLE_WORLD_COORDINATE } @@ -303,8 +312,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box #if ENABLE_WORLD_COORDINATE void GLGizmoMove3D::transform_to_local(const Selection& selection) const { - const Vec3d center = selection.get_bounding_box().center(); - glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); if (!wxGetApp().obj_manipul()->get_world_coordinates()) { const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); @@ -312,20 +320,23 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const } } -BoundingBoxf3 GLGizmoMove3D::get_selection_box() +void GLGizmoMove3D::calc_selection_box_and_center() { const Selection& selection = m_parent.get_selection(); - BoundingBoxf3 box; - if (wxGetApp().obj_manipul()->get_world_coordinates()) - box = selection.get_bounding_box(); + if (wxGetApp().obj_manipul()->get_world_coordinates()) { + m_bounding_box = selection.get_bounding_box(); + m_center = m_bounding_box.center(); + } else { + m_bounding_box.reset(); const Selection::IndicesList& ids = selection.get_volume_idxs(); for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); + const GLVolume& v = *selection.get_volume(id); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); } + m_bounding_box = m_bounding_box.transformed(selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(true, true, false, true)); + m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); } - return box; } #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 8f9f6ea271..fac9955f01 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -12,6 +12,10 @@ class GLGizmoMove3D : public GLGizmoBase static const double Offset; Vec3d m_displacement{ Vec3d::Zero() }; +#if ENABLE_WORLD_COORDINATE + Vec3d m_center{ Vec3d::Zero() }; + BoundingBoxf3 m_bounding_box; +#endif // ENABLE_WORLD_COORDINATE double m_snap_step{ 1.0 }; Vec3d m_starting_drag_position{ Vec3d::Zero() }; Vec3d m_starting_box_center{ Vec3d::Zero() }; @@ -44,7 +48,7 @@ private: void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const; #if ENABLE_WORLD_COORDINATE void transform_to_local(const Selection& selection) const; - BoundingBoxf3 get_selection_box(); + void calc_selection_box_and_center(); #endif // ENABLE_WORLD_COORDINATE }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 3db0e3ddb2..78d28a5f7b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -207,17 +207,18 @@ void GLGizmoRotate::on_render_for_picking() void GLGizmoRotate::init_data_from_selection(const Selection& selection) { #if ENABLE_WORLD_COORDINATE - m_bounding_box.reset(); if (wxGetApp().obj_manipul()->get_world_coordinates()) { m_bounding_box = selection.get_bounding_box(); m_center = m_bounding_box.center(); } else { + m_bounding_box.reset(); const Selection::IndicesList& ids = selection.get_volume_idxs(); for (unsigned int id : ids) { const GLVolume* v = selection.get_volume(id); - m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_instance_transformation().get_matrix(true, true, false, true) * v->get_volume_transformation().get_matrix())); + m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); } + m_bounding_box = m_bounding_box.transformed(selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(true, true, false, true)); m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); } m_radius = Offset + m_bounding_box.radius(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 8cf0796f66..972d9e5f69 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -89,12 +89,12 @@ void GLGizmoScale3D::on_start_dragging() m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); #if ENABLE_WORLD_COORDINATE m_starting.drag_position = m_grabbers_transform * m_grabbers[m_hover_id].center; - m_starting.box = m_box; + m_starting.box = m_bounding_box; m_starting.center = m_center; - m_starting.transform = wxGetApp().obj_manipul()->get_world_coordinates() ? Transform3d::Identity() : m_transform; + m_starting.instance_center = m_instance_center; #else m_starting.drag_position = m_grabbers[m_hover_id].center; - m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_box : m_parent.get_selection().get_bounding_box(); + m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_bounding_box : m_parent.get_selection().get_bounding_box(); const Vec3d center = m_starting.box.center(); m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z()); @@ -126,13 +126,15 @@ void GLGizmoScale3D::on_render() glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); - m_box.reset(); - m_transform = Transform3d::Identity(); + m_bounding_box.reset(); #if ENABLE_WORLD_COORDINATE m_grabbers_transform = Transform3d::Identity(); + m_center = Vec3d::Zero(); + m_instance_center = Vec3d::Zero(); bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); if (selection.is_single_full_instance() && !world_coordinates) { #else + m_transform = Transform3d::Identity(); // Transforms grabbers' offsets to world refefence system Transform3d offsets_transform = Transform3d::Identity(); m_offsets_transform = Transform3d::Identity(); @@ -143,31 +145,31 @@ void GLGizmoScale3D::on_render() // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { - const GLVolume* v = selection.get_volume(idx); + const GLVolume& v = *selection.get_volume(idx); #if ENABLE_WORLD_COORDINATE - m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); #else - m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE - m_box = m_box.transformed(selection.get_volume(*idxs.begin())->get_instance_transformation().get_matrix(true, true, false, true)); + m_bounding_box = m_bounding_box.transformed(selection.get_volume(*idxs.begin())->get_instance_transformation().get_matrix(true, true, false, true)); #endif // ENABLE_WORLD_COORDINATE // gets transform from first selected volume - const GLVolume* v = selection.get_volume(*idxs.begin()); + const GLVolume& v = *selection.get_volume(*idxs.begin()); #if ENABLE_WORLD_COORDINATE - m_transform = v->get_instance_transformation().get_matrix(false, false, true); - m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * Geometry::assemble_transform(m_box.center()); - m_center = v->get_instance_offset(); + m_grabbers_transform = v.get_instance_transformation().get_matrix(false, false, true) * Geometry::assemble_transform(m_bounding_box.center()); + m_center = selection.get_volume(*idxs.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); + m_instance_center = v.get_instance_offset(); #else - m_transform = v->get_instance_transformation().get_matrix(); + m_transform = v.get_instance_transformation().get_matrix(); // gets angles from first selected volume - angles = v->get_instance_rotation(); + angles = v.get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v.get_instance_mirror()); m_offsets_transform = offsets_transform; #endif // ENABLE_WORLD_COORDINATE } @@ -176,34 +178,31 @@ void GLGizmoScale3D::on_render() #else else if (selection.is_single_modifier() || selection.is_single_volume()) { #endif // ENABLE_WORLD_COORDINATE - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_WORLD_COORDINATE - m_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix(true, true, false, true))); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true))); + Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true) * v.get_volume_transformation().get_matrix(true, false, true)); + trafo.set_offset(v.world_matrix().translation()); + m_grabbers_transform = trafo.get_matrix(); + m_center = v.world_matrix() * m_bounding_box.center(); + m_instance_center = m_center; + } + else { + m_bounding_box = selection.get_bounding_box(); + m_grabbers_transform = Geometry::assemble_transform(m_bounding_box.center()); + m_center = m_bounding_box.center(); + m_instance_center = selection.is_single_full_instance() ? selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_offset() : m_center; + } #else - m_box = v->bounding_box(); -#endif // ENABLE_WORLD_COORDINATE -#if ENABLE_WORLD_COORDINATE - m_transform = v->world_matrix(); - m_grabbers_transform = v->get_instance_transformation().get_matrix(false, false, true) * v->get_volume_transformation().get_matrix(false, false, true); - m_center = v->world_matrix() * m_box.center(); -#else - m_transform = v->world_matrix(); + m_bounding_box = v.bounding_box(); + m_transform = v.world_matrix(); angles = Geometry::extract_euler_angles(m_transform); // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); - m_offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), v->get_volume_rotation(), Vec3d::Ones(), v->get_volume_mirror()); -#endif // ENABLE_WORLD_COORDINATE + offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v.get_instance_mirror()); + m_offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), v.get_volume_rotation(), Vec3d::Ones(), v.get_volume_mirror()); } -#if ENABLE_WORLD_COORDINATE - else { - m_box = selection.get_bounding_box(); - m_transform = Geometry::assemble_transform(m_box.center()); - m_grabbers_transform = m_transform; - m_center = selection.is_single_full_instance() ? selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_offset() : m_box.center(); - } -#else else - m_box = selection.get_bounding_box(); + m_bounding_box = selection.get_bounding_box(); Vec3d offset_x = offsets_transform * (Offset * Vec3d::UnitX()); Vec3d offset_y = offsets_transform * (Offset * Vec3d::UnitY()); @@ -212,52 +211,29 @@ void GLGizmoScale3D::on_render() bool ctrl_down = m_dragging && m_starting.ctrl_down || !m_dragging && wxGetKeyState(WXK_CONTROL); #endif // ENABLE_WORLD_COORDINATE - // x axis #if ENABLE_WORLD_COORDINATE - const Vec3d box_half_size = 0.5 * m_box.size(); + // x axis + const Vec3d box_half_size = 0.5 * m_bounding_box.size(); bool use_constrain = wxGetKeyState(WXK_CONTROL) && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); m_grabbers[0].center = { -(box_half_size.x() + Offset), 0.0, 0.0 }; m_grabbers[0].color = (use_constrain && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; m_grabbers[1].center = { box_half_size.x() + Offset, 0.0, 0.0 }; m_grabbers[1].color = (use_constrain && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; -#else - const Vec3d center = m_box.center(); - - m_grabbers[0].center = m_transform * Vec3d(m_box.min.x(), center.y(), center.z()) - offset_x; - m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; - m_grabbers[1].center = m_transform * Vec3d(m_box.max.x(), center.y(), center.z()) + offset_x; - m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; -#endif // ENABLE_WORLD_COORDINATE // y axis -#if ENABLE_WORLD_COORDINATE m_grabbers[2].center = { 0.0, -(box_half_size.y() + Offset), 0.0 }; m_grabbers[2].color = (use_constrain && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; m_grabbers[3].center = { 0.0, box_half_size.y() + Offset, 0.0 }; m_grabbers[3].color = (use_constrain && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; -#else - m_grabbers[2].center = m_transform * Vec3d(center.x(), m_box.min.y(), center.z()) - offset_y; - m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; - m_grabbers[3].center = m_transform * Vec3d(center.x(), m_box.max.y(), center.z()) + offset_y; - m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; -#endif // ENABLE_WORLD_COORDINATE // z axis -#if ENABLE_WORLD_COORDINATE m_grabbers[4].center = { 0.0, 0.0, -(box_half_size.z() + Offset) }; m_grabbers[4].color = (use_constrain && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; m_grabbers[5].center = { 0.0, 0.0, box_half_size.z() + Offset }; m_grabbers[5].color = (use_constrain && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; -#else - m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_box.min.z()) - offset_z; - m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; - m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_box.max.z()) + offset_z; - m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; -#endif // ENABLE_WORLD_COORDINATE // uniform -#if ENABLE_WORLD_COORDINATE m_grabbers[6].center = { -(box_half_size.x() + Offset), -(box_half_size.y() + Offset), 0.0 }; m_grabbers[6].color = (use_constrain && m_hover_id == 8) ? CONSTRAINED_COLOR : m_highlight_color; m_grabbers[7].center = { box_half_size.x() + Offset, -(box_half_size.y() + Offset), 0.0 }; @@ -267,28 +243,47 @@ void GLGizmoScale3D::on_render() m_grabbers[9].center = { -(box_half_size.x() + Offset), box_half_size.y() + Offset, 0.0 }; m_grabbers[9].color = (use_constrain && m_hover_id == 7) ? CONSTRAINED_COLOR : m_highlight_color; #else - m_grabbers[6].center = m_transform * Vec3d(m_box.min.x(), m_box.min.y(), center.z()) - offset_x - offset_y; - m_grabbers[7].center = m_transform * Vec3d(m_box.max.x(), m_box.min.y(), center.z()) + offset_x - offset_y; - m_grabbers[8].center = m_transform * Vec3d(m_box.max.x(), m_box.max.y(), center.z()) + offset_x + offset_y; - m_grabbers[9].center = m_transform * Vec3d(m_box.min.x(), m_box.max.y(), center.z()) - offset_x + offset_y; + // x axis + const Vec3d center = m_bounding_box.center(); + + m_grabbers[0].center = m_transform * Vec3d(m_bounding_box.min.x(), center.y(), center.z()) - offset_x; + m_grabbers[0].color = (ctrl_down && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + m_grabbers[1].center = m_transform * Vec3d(m_bounding_box.max.x(), center.y(), center.z()) + offset_x; + m_grabbers[1].color = (ctrl_down && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + + // y axis + m_grabbers[2].center = m_transform * Vec3d(center.x(), m_bounding_box.min.y(), center.z()) - offset_y; + m_grabbers[2].color = (ctrl_down && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + m_grabbers[3].center = m_transform * Vec3d(center.x(), m_bounding_box.max.y(), center.z()) + offset_y; + m_grabbers[3].color = (ctrl_down && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + + // z axis + m_grabbers[4].center = m_transform * Vec3d(center.x(), center.y(), m_bounding_box.min.z()) - offset_z; + m_grabbers[4].color = (ctrl_down && m_hover_id == 5) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + m_grabbers[5].center = m_transform * Vec3d(center.x(), center.y(), m_bounding_box.max.z()) + offset_z; + m_grabbers[5].color = (ctrl_down && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + + // uniform + m_grabbers[6].center = m_transform * Vec3d(m_bounding_box.min.x(), m_bounding_box.min.y(), center.z()) - offset_x - offset_y; + m_grabbers[7].center = m_transform * Vec3d(m_bounding_box.max.x(), m_bounding_box.min.y(), center.z()) + offset_x - offset_y; + m_grabbers[8].center = m_transform * Vec3d(m_bounding_box.max.x(), m_bounding_box.max.y(), center.z()) + offset_x + offset_y; + m_grabbers[9].center = m_transform * Vec3d(m_bounding_box.min.x(), m_bounding_box.max.y(), center.z()) - offset_x + offset_y; for (int i = 6; i < 10; ++i) { m_grabbers[i].color = m_highlight_color; } -#endif // ENABLE_WORLD_COORDINATE -#if !ENABLE_WORLD_COORDINATE // sets grabbers orientation for (int i = 0; i < 10; ++i) { m_grabbers[i].angles = angles; } -#endif // !ENABLE_WORLD_COORDINATE +#endif // ENABLE_WORLD_COORDINATE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); #if ENABLE_WORLD_COORDINATE glsafe(::glPushMatrix()); transform_to_local(selection); - float grabber_mean_size = (float)((m_box.size().x() + m_box.size().y() + m_box.size().z()) / 3.0); + float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0); #else const BoundingBoxf3& selection_box = selection.get_bounding_box(); float grabber_mean_size = (float)((selection_box.size().x() + selection_box.size().y() + selection_box.size().z()) / 3.0); @@ -392,7 +387,7 @@ void GLGizmoScale3D::on_render_for_picking() #if ENABLE_WORLD_COORDINATE glsafe(::glPushMatrix()); transform_to_local(m_parent.get_selection()); - render_grabbers_for_picking(m_box); + render_grabbers_for_picking(m_bounding_box); glsafe(::glPopMatrix()); #else render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); @@ -419,27 +414,33 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) Vec3d starting_scale = m_starting.scale; const Selection& selection = m_parent.get_selection(); const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); - if (selection.is_single_full_instance() && world_coordinates) { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); - curr_scale = (m * curr_scale).cwiseAbs(); - starting_scale = (m * starting_scale).cwiseAbs(); - } - else if ((selection.is_single_volume() || selection.is_single_modifier()) && world_coordinates) { - const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); - const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); - const Transform3d m = mi * mv; - curr_scale = (m * curr_scale).cwiseAbs(); - starting_scale = (m * starting_scale).cwiseAbs(); + if (world_coordinates) { + if (selection.is_single_full_instance()) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); + curr_scale = (m * curr_scale).cwiseAbs(); + starting_scale = (m * starting_scale).cwiseAbs(); + } + else if (selection.is_single_volume() || selection.is_single_modifier()) { + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + const Transform3d m = mi * mv; + curr_scale = (m * curr_scale).cwiseAbs(); + starting_scale = (m * starting_scale).cwiseAbs(); + } } curr_scale(axis) = starting_scale(axis) * ratio; - if (selection.is_single_full_instance() && world_coordinates) - m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); - else if ((selection.is_single_volume() || selection.is_single_modifier()) && world_coordinates) { - const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); - const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); - m_scale = (mv * mi * curr_scale).cwiseAbs(); + if (world_coordinates) { + if (selection.is_single_full_instance()) + m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); + else if (selection.is_single_volume() || selection.is_single_modifier()) { + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); + m_scale = (mv * mi * curr_scale).cwiseAbs(); + } + else + m_scale = curr_scale; } else m_scale = curr_scale; @@ -459,7 +460,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) local_offset *= -1.0; #if ENABLE_WORLD_COORDINATE - Vec3d center_offset = m_starting.center - m_starting.transform * m_starting.box.center(); + Vec3d center_offset = m_starting.instance_center - m_starting.center; if (selection.is_single_full_instance() && !world_coordinates) { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); center_offset = m * center_offset; @@ -476,8 +477,9 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) } if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); - m_offset = m * m_offset; + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); + m_offset = mv * mi * m_offset; } #else Vec3d local_offset_vec; @@ -513,23 +515,25 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) const Selection& selection = m_parent.get_selection(); const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); - Vec3d center_offset = m_starting.center - m_starting.transform * m_starting.box.center(); + Vec3d center_offset = m_starting.instance_center - m_starting.center; + if (selection.is_single_full_instance() && !world_coordinates) { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); center_offset = m * center_offset; } - + m_offset += (ratio - 1.0) * center_offset; if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); - m_offset = m * m_offset; + const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); + const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); + m_offset = mv * mi * m_offset; } } else #endif // ENABLE_WORLD_COORDINATE m_offset = Vec3d::Zero(); -} + } } double GLGizmoScale3D::calc_ratio(const UpdateData& data) const @@ -570,8 +574,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const #if ENABLE_WORLD_COORDINATE void GLGizmoScale3D::transform_to_local(const Selection& selection) const { - const Vec3d center = selection.get_bounding_box().center(); - glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); if (!wxGetApp().obj_manipul()->get_world_coordinates()) { Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index c011e4bff2..19d180d9c8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -19,7 +19,7 @@ class GLGizmoScale3D : public GLGizmoBase Vec3d drag_position{ Vec3d::Zero() }; #if ENABLE_WORLD_COORDINATE Vec3d center{ Vec3d::Zero() }; - Transform3d transform; + Vec3d instance_center{ Vec3d::Zero() }; #endif // ENABLE_WORLD_COORDINATE BoundingBoxf3 box; #if !ENABLE_WORLD_COORDINATE @@ -27,12 +27,13 @@ class GLGizmoScale3D : public GLGizmoBase #endif // !ENABLE_WORLD_COORDINATE }; - BoundingBoxf3 m_box; - Transform3d m_transform; + BoundingBoxf3 m_bounding_box; #if ENABLE_WORLD_COORDINATE Transform3d m_grabbers_transform; Vec3d m_center{ Vec3d::Zero() }; + Vec3d m_instance_center{ Vec3d::Zero() }; #else + Transform3d m_transform; // Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes) Transform3d m_offsets_transform; #endif // ENABLE_WORLD_COORDINATE From 41fc0be32add44034055e6267b104d64993114fd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Nov 2021 14:50:30 +0100 Subject: [PATCH 31/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - 1st installment: introduction of instance reference system in part manipulation --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GUI_ObjectList.cpp | 4 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 169 ++++++++++++++++++++-- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 32 +++- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 12 ++ src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 14 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 34 ++++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 20 +++ src/slic3r/GUI/Plater.cpp | 6 +- src/slic3r/GUI/Selection.cpp | 29 +++- src/slic3r/GUI/Selection.hpp | 3 + 11 files changed, 296 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4564d975d2..9e3d088c48 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -88,6 +88,8 @@ #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4) // Enable showing world coordinates of volumes' offset relative to the instance containing them #define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (1 && ENABLE_WORLD_COORDINATE) +// Enable editing instance coordinates of volumes +#define ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES (1 && ENABLE_WORLD_COORDINATE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index e3f504fc2e..7da4272c74 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3266,7 +3266,11 @@ void ObjectList::update_selections() return; sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); } +#if ENABLE_WORLD_COORDINATE + else if (selection.is_single_volume_or_modifier()) { +#else else if (selection.is_single_volume() || selection.is_any_modifier()) { +#endif // ENABLE_WORLD_COORDINATE const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin()); if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx()) return; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b015094b44..6139707e62 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -52,10 +52,17 @@ static choice_ctrl* create_word_local_combo(wxWindow *parent) temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::World)); + temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Instance)); + temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Local)); + temp->Select((int)ObjectManipulation::ECoordinatesType::World); +#else temp->Append(_L("World coordinates")); temp->Append(_L("Local coordinates")); temp->SetSelection(0); temp->SetValue(temp->GetString(0)); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES temp->SetToolTip(_L("Select coordinate space, in which the transformation will be performed.")); return temp; @@ -81,8 +88,14 @@ void msw_rescale_word_local_combo(choice_ctrl* combo) // Set rescaled size combo->SetSize(size); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::World)); + combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Instance)); + combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Local)); +#else combo->Append(_L("World coordinates")); combo->Append(_L("Local coordinates")); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES combo->SetValue(selection); #else @@ -101,6 +114,7 @@ static void set_font_and_background_style(wxWindow* win, const wxFont& font) static const wxString axes_color_text[] = { "#990000", "#009900", "#000099" }; static const wxString axes_color_back[] = { "#f5dcdc", "#dcf5dc", "#dcdcf5" }; + ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) { @@ -157,7 +171,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Add world local combobox m_word_local_combo = create_word_local_combo(parent); m_word_local_combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent& evt) { +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + this->set_coordinates_type(evt.GetString()); +#else this->set_world_coordinates(evt.GetSelection() != 1); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES }), m_word_local_combo->GetId()); // Small trick to correct layouting in different view_mode : @@ -264,7 +282,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_volume_or_modifier()) { +#else if (selection.is_single_volume() || selection.is_single_modifier()) { +#endif // ENABLE_WORLD_COORDINATE GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); } @@ -327,11 +349,15 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); - if (selection.is_single_volume() || selection.is_single_modifier()) { #if ENABLE_WORLD_COORDINATE - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + if (selection.is_single_volume_or_modifier()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const double min_z = get_volume_min_z(*volume); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!is_world_coordinates()) { +#else if (!m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ()); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed")); @@ -344,6 +370,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_position_value(2, m_cache.position.z() - min_z); } #else + if (selection.is_single_volume() || selection.is_single_modifier()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (get_volume_min_z(*volume) * Vec3d::UnitZ()); @@ -356,7 +383,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else if (selection.is_single_full_instance()) { #if ENABLE_WORLD_COORDINATE const double min_z = selection.get_scaled_instance_bounding_box().min.z(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!is_world_coordinates()) { +#else if (!m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ()); @@ -396,7 +427,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_volume_or_modifier()) { +#else if (selection.is_single_volume() || selection.is_single_modifier()) { +#endif // ENABLE_WORLD_COORDINATE GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); volume->set_volume_rotation(Vec3d::Zero()); } @@ -477,7 +512,19 @@ void ObjectManipulation::Show(const bool show) // Show the "World Coordinates" / "Local Coordintes" Combo in Advanced / Expert mode only. #if ENABLE_WORLD_COORDINATE const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); + bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (selection.is_single_volume_or_modifier() && m_word_local_combo->GetCount() < 3) { + m_word_local_combo->Insert(coordinate_type_str(ECoordinatesType::Instance), wxNullBitmap, 1); + m_word_local_combo->Select((int)ECoordinatesType::World); + this->set_coordinates_type(m_word_local_combo->GetStringSelection()); + } + else if (selection.is_single_full_instance() && m_word_local_combo->GetCount() > 2) { + m_word_local_combo->Delete(1); + m_word_local_combo->Select((int)ECoordinatesType::World); + this->set_coordinates_type(m_word_local_combo->GetStringSelection()); + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; #endif // ENABLE_WORLD_COORDINATE @@ -558,8 +605,10 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_rotate_label_string = L("Rotation"); m_new_scale_label_string = L("Scale factors"); +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (wxGetApp().get_mode() == comSimple) m_world_coordinates = true; +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES ObjectList* obj_list = wxGetApp().obj_list(); if (selection.is_single_full_instance()) { @@ -570,14 +619,22 @@ void ObjectManipulation::update_settings_value(const Selection& selection) #endif // !ENABLE_WORLD_COORDINATE // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. - if (m_world_coordinates && ! m_uniform_scale && +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (is_world_coordinates() && !m_uniform_scale && +#else + if (m_world_coordinates && ! m_uniform_scale && +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES ! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) { // Manipulating an instance in the world coordinate system, rotation is not multiples of ninety degrees, therefore enforce uniform scaling. m_uniform_scale = true; m_lock_bnt->SetLock(true); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (is_world_coordinates()) { +#else if (m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #if ENABLE_WORLD_COORDINATE m_new_position = volume->get_instance_offset(); #endif // ENABLE_WORLD_COORDINATE @@ -613,11 +670,19 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_scale_label_string = L("Scale"); m_new_enabled = true; } +#if ENABLE_WORLD_COORDINATE + else if (selection.is_single_volume_or_modifier()) { +#else else if (selection.is_single_modifier() || selection.is_single_volume()) { +#endif // ENABLE_WORLD_COORDINATE // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (is_world_coordinates()) { +#else if (m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Geometry::Transformation trafo(volume->world_matrix()); #if ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET @@ -722,11 +787,13 @@ void ObjectManipulation::update_if_dirty() m_lock_bnt->enable(); } - { +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + { int new_selection = m_world_coordinates ? 0 : 1; if (m_word_local_combo->GetSelection() != new_selection) m_word_local_combo->SetSelection(new_selection); } +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (m_new_enabled) m_og->enable(); @@ -758,12 +825,14 @@ void ObjectManipulation::update_reset_buttons_visibility() bool show_scale = false; bool show_drop_to_bed = false; - if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_WORLD_COORDINATE + if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Vec3d rotation = Vec3d::Zero(); Vec3d scale = Vec3d::Ones(); #else + if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Vec3d rotation; Vec3d scale; #endif // ENABLE_WORLD_COORDINATE @@ -815,8 +884,16 @@ void ObjectManipulation::update_mirror_buttons_visibility() Selection& selection = canvas->get_selection(); std::array new_states = {mbHidden, mbHidden, mbHidden}; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!is_world_coordinates()) { +#else if (!m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { +#else if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { +#endif // ENABLE_WORLD_COORDINATE const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Vec3d mirror; @@ -881,6 +958,19 @@ void ObjectManipulation::update_warning_icon_state(const MeshErrorsInfo& warning m_fix_throught_netfab_bitmap->SetToolTip(tooltip); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type) +{ + switch (type) + { + case ECoordinatesType::World: { return _L("World coordinates"); } + case ECoordinatesType::Instance: { return _L("Instance coordinates"); } + case ECoordinatesType::Local: { return _L("Local coordinates"); } + default: { assert(false); break; } + } +} +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + void ObjectManipulation::reset_settings_value() { m_new_position = Vec3d::Zero(); @@ -905,7 +995,11 @@ void ObjectManipulation::change_position_value(int axis, double value) Selection& selection = canvas->get_selection(); selection.start_dragging(); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + selection.translate(position - m_cache.position, !is_world_coordinates()); +#else selection.translate(position - m_cache.position, !m_world_coordinates); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else selection.translate(position - m_cache.position, selection.requires_local_axes()); #endif // ENABLE_WORLD_COORDINATE @@ -935,7 +1029,11 @@ void ObjectManipulation::change_rotation_value(int axis, double value) if (selection.is_single_full_instance()) transformation_type.set_independent(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!is_world_coordinates()) { +#else if (!m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES //FIXME Selection::rotate() does not process absolute rotations correctly: It does not recognize the axis index, which was changed. // transformation_type.set_absolute(); transformation_type.set_local(); @@ -991,7 +1089,11 @@ void ObjectManipulation::change_size_value(int axis, double value) const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); Vec3d ref_size = m_cache.size; +#if ENABLE_WORLD_COORDINATE + if (selection.is_single_volume_or_modifier()) { +#else if (selection.is_single_volume() || selection.is_single_modifier()) { +#endif // ENABLE_WORLD_COORDINATE const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); const Vec3d local_size = size.cwiseQuotient(v->get_instance_scaling_factor()); const Vec3d local_ref_size = v->bounding_box().size().cwiseProduct(v->get_volume_scaling_factor()); @@ -1001,7 +1103,11 @@ void ObjectManipulation::change_size_value(int axis, double value) ref_size = Vec3d::Ones(); } else if (selection.is_single_full_instance()) - ref_size = m_world_coordinates ? +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + ref_size = is_world_coordinates() ? +#else + ref_size = m_world_coordinates ? +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES selection.get_unscaled_instance_bounding_box().size() : wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size(); @@ -1021,16 +1127,24 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const #if ENABLE_WORLD_COORDINATE TransformationType transformation_type; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!is_world_coordinates()) +#else if (!m_world_coordinates) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES transformation_type.set_local(); bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale(); Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!uniform_scale && is_world_coordinates()) { +#else if (!uniform_scale && m_world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_full_instance()) scaling_factor = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * scaling_factor).cwiseAbs(); - else if (selection.is_single_volume() || selection.is_single_modifier()) { + else if (selection.is_single_volume_or_modifier()) { const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); scaling_factor = (mv * mi * scaling_factor).cwiseAbs(); @@ -1077,7 +1191,11 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double void ObjectManipulation::set_uniform_scaling(const bool new_value) { const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection(); - if (selection.is_single_full_instance() && m_world_coordinates && !new_value) { +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (selection.is_single_full_instance() && is_world_coordinates() && !new_value) { +#else + if (selection.is_single_full_instance() && m_world_coordinates && !new_value) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); @@ -1110,6 +1228,23 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) } #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +void ObjectManipulation::set_coordinates_type(ECoordinatesType type) +{ + if (wxGetApp().get_mode() == comSimple) + type = ECoordinatesType::World; + + if (m_coordinates_type == type) + return; + + m_coordinates_type = type; + this->UpdateAndShow(true); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->get_gizmos_manager().update_data(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); +} +#else void ObjectManipulation::set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; @@ -1126,6 +1261,7 @@ bool ObjectManipulation::get_world_coordinates() const return wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()) ? m_world_coordinates : true; } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #endif // ENABLE_WORLD_COORDINATE void ObjectManipulation::msw_rescale() @@ -1191,6 +1327,19 @@ void ObjectManipulation::sys_color_changed() m_mirror_buttons[id].first->msw_rescale(); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +void ObjectManipulation::set_coordinates_type(const wxString& type_string) +{ + ECoordinatesType type = ECoordinatesType::World; + if (type_string == coordinate_type_str(ECoordinatesType::Instance)) + type = ECoordinatesType::Instance; + else if (type_string == coordinate_type_str(ECoordinatesType::Local)) + type = ECoordinatesType::Local; + + this->set_coordinates_type(type); +} +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + static const char axes[] = { 'x', 'y', 'z' }; ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, const std::string& opt_key, diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 849a443f43..d0c38cc477 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -72,6 +72,15 @@ public: static const double in_to_mm; static const double mm_to_in; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + enum class ECoordinatesType : unsigned char + { + World, + Instance, + Local + }; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + private: struct Cache { @@ -148,8 +157,12 @@ private: Vec3d m_new_size; bool m_new_enabled {true}; bool m_uniform_scale {true}; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + ECoordinatesType m_coordinates_type{ ECoordinatesType::World }; +#else // Does the object manipulation panel work in World or Local coordinates? bool m_world_coordinates = true; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES LockButton* m_lock_bnt{ nullptr }; choice_ctrl* m_word_local_combo { nullptr }; @@ -191,11 +204,20 @@ public: void set_uniform_scaling(const bool uniform_scale); bool get_uniform_scaling() const { return m_uniform_scale; } - // Does the object manipulation panel work in World or Local coordinates? #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + void set_coordinates_type(ECoordinatesType type); + ECoordinatesType get_coordinates_type() const { return m_coordinates_type; } + bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; } + bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; } + bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; } +#else + // Does the object manipulation panel work in World or Local coordinates? void set_world_coordinates(const bool world_coordinates); bool get_world_coordinates() const; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else + // Does the object manipulation panel work in World or Local coordinates? void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); } bool get_world_coordinates() const { return m_world_coordinates; } #endif // ENABLE_WORLD_COORDINATE @@ -227,6 +249,10 @@ public: ManipulationEditor* get_focused_editor() { return m_focused_editor; } #endif // ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + static wxString coordinate_type_str(ECoordinatesType type); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + private: void reset_settings_value(); void update_settings_value(const Selection& selection); @@ -242,6 +268,10 @@ private: void change_scale_value(int axis, double value); void change_size_value(int axis, double value); void do_scale(int axis, const Vec3d &scale) const; + +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + void set_coordinates_type(const wxString& type_string); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES }; }} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 1af59c8e0c..88cb841372 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -74,7 +74,11 @@ void GLGizmoMove3D::on_start_dragging() if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (wxGetApp().obj_manipul()->is_world_coordinates()) +#else if (wxGetApp().obj_manipul()->get_world_coordinates()) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_starting_drag_position = m_center + m_grabbers[m_hover_id].center; else { const Selection& selection = m_parent.get_selection(); @@ -314,7 +318,11 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const { glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (!wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } @@ -323,7 +331,11 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const void GLGizmoMove3D::calc_selection_box_and_center() { const Selection& selection = m_parent.get_selection(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_bounding_box = selection.get_bounding_box(); m_center = m_bounding_box.center(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 78d28a5f7b..4d82f5b9cf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -206,8 +206,11 @@ void GLGizmoRotate::on_render_for_picking() #if ENABLE_WORLD_COORDINATE void GLGizmoRotate::init_data_from_selection(const Selection& selection) { -#if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_bounding_box = selection.get_bounding_box(); m_center = m_bounding_box.center(); } @@ -222,17 +225,16 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); } m_radius = Offset + m_bounding_box.radius(); -#else - const BoundingBoxf3& box = selection.get_bounding_box(); - m_center = box.center(); - m_radius = Offset + box.radius(); -#endif // ENABLE_WORLD_COORDINATE m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; m_snap_fine_in_radius = m_radius; m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (wxGetApp().obj_manipul()->is_world_coordinates()) +#else if (wxGetApp().obj_manipul()->get_world_coordinates()) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_orient_matrix = Transform3d::Identity(); else m_orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 972d9e5f69..f62ed9320d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -28,7 +28,11 @@ std::string GLGizmoScale3D::get_tooltip() const Vec3d scale = 100.0 * Vec3d::Ones(); if (selection.is_single_full_instance()) scale = 100.0 * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor(); +#if ENABLE_WORLD_COORDINATE + else if (selection.is_single_volume_or_modifier()) +#else else if (selection.is_single_modifier() || selection.is_single_volume()) +#endif // ENABLE_WORLD_COORDINATE scale = 100.0 * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor(); if (m_hover_id == 0 || m_hover_id == 1 || m_grabbers[0].dragging || m_grabbers[1].dragging) @@ -131,7 +135,11 @@ void GLGizmoScale3D::on_render() m_grabbers_transform = Transform3d::Identity(); m_center = Vec3d::Zero(); m_instance_center = Vec3d::Zero(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); +#else bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_full_instance() && !world_coordinates) { #else m_transform = Transform3d::Identity(); @@ -174,7 +182,7 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE - else if ((selection.is_single_modifier() || selection.is_single_volume()) && !world_coordinates) { + else if (selection.is_single_volume_or_modifier() && !world_coordinates) { #else else if (selection.is_single_modifier() || selection.is_single_volume()) { #endif // ENABLE_WORLD_COORDINATE @@ -214,7 +222,7 @@ void GLGizmoScale3D::on_render() #if ENABLE_WORLD_COORDINATE // x axis const Vec3d box_half_size = 0.5 * m_bounding_box.size(); - bool use_constrain = wxGetKeyState(WXK_CONTROL) && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); + bool use_constrain = wxGetKeyState(WXK_CONTROL) && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()); m_grabbers[0].center = { -(box_half_size.x() + Offset), 0.0, 0.0 }; m_grabbers[0].color = (use_constrain && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; @@ -413,14 +421,18 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) Vec3d curr_scale = m_scale; Vec3d starting_scale = m_starting.scale; const Selection& selection = m_parent.get_selection(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + const bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); +#else const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (world_coordinates) { if (selection.is_single_full_instance()) { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); curr_scale = (m * curr_scale).cwiseAbs(); starting_scale = (m * starting_scale).cwiseAbs(); } - else if (selection.is_single_volume() || selection.is_single_modifier()) { + else if (selection.is_single_volume_or_modifier()) { const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); const Transform3d m = mi * mv; @@ -434,7 +446,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (world_coordinates) { if (selection.is_single_full_instance()) m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); - else if (selection.is_single_volume() || selection.is_single_modifier()) { + else if (selection.is_single_volume_or_modifier()) { const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse(); m_scale = (mv * mi * curr_scale).cwiseAbs(); @@ -476,7 +488,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: { m_offset = Vec3d::Zero(); break; } } - if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { + if (selection.is_single_volume_or_modifier() && !world_coordinates) { const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); m_offset = mv * mi * m_offset; @@ -514,7 +526,11 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) m_offset.y() *= -1.0; const Selection& selection = m_parent.get_selection(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + const bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); +#else const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Vec3d center_offset = m_starting.instance_center - m_starting.center; if (selection.is_single_full_instance() && !world_coordinates) { @@ -524,7 +540,7 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) m_offset += (ratio - 1.0) * center_offset; - if ((selection.is_single_volume() || selection.is_single_modifier()) && !world_coordinates) { + if (selection.is_single_volume_or_modifier() && !world_coordinates) { const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); m_offset = mv * mi * m_offset; @@ -576,9 +592,13 @@ void GLGizmoScale3D::transform_to_local(const Selection& selection) const { glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z())); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (!wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); - if (selection.is_single_volume() || selection.is_single_modifier()) + if (selection.is_single_volume_or_modifier()) orient_matrix = orient_matrix * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index dcffa38463..632070485e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -265,7 +265,11 @@ void GLGizmosManager::update_data() set_sla_support_data(model_object); set_painter_gizmo_data(); } +#if ENABLE_WORLD_COORDINATE + else if (selection.is_single_volume_or_modifier()) { +#else else if (selection.is_single_volume() || selection.is_single_modifier()) { +#endif // ENABLE_WORLD_COORDINATE const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); set_scale(volume->get_volume_scaling_factor()); set_rotation(Vec3d::Zero()); @@ -617,7 +621,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { // Apply new temporary offset #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + selection.translate(get_displacement(), !wxGetApp().obj_manipul()->is_world_coordinates()); +#else selection.translate(get_displacement(), !wxGetApp().obj_manipul()->get_world_coordinates()); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else selection.translate(get_displacement()); #endif // ENABLE_WORLD_COORDINATE @@ -629,7 +637,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // Apply new temporary scale factors #if ENABLE_WORLD_COORDINATE TransformationType transformation_type; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!wxGetApp().obj_manipul()->is_world_coordinates()) +#else if (!wxGetApp().obj_manipul()->get_world_coordinates()) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES transformation_type.set_local(); #else TransformationType transformation_type(TransformationType::Local_Absolute_Joint); @@ -639,7 +651,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) selection.scale(get_scale(), transformation_type); if (control_down) #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->is_world_coordinates()); +#else selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->get_world_coordinates()); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else selection.translate(get_scale_offset(), true); #endif // ENABLE_WORLD_COORDINATE @@ -650,7 +666,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { // Apply new temporary rotations #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + TransformationType transformation_type = wxGetApp().obj_manipul()->is_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint; +#else TransformationType transformation_type(wxGetApp().obj_manipul()->get_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else TransformationType transformation_type(TransformationType::World_Relative_Joint); #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 710a6ffd25..1a2c72abcf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4246,8 +4246,12 @@ void Plater::priv::on_right_click(RBtnEvent& evt) const bool is_some_full_instances = selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_multiple_full_instance(); +#if ENABLE_WORLD_COORDINATE + const bool is_part = selection.is_single_volume_or_modifier(); +#else const bool is_part = selection.is_single_volume() || selection.is_single_modifier(); - menu = is_some_full_instances ? menus.object_menu() : +#endif // ENABLE_WORLD_COORDINATE + menu = is_some_full_instances ? menus.object_menu() : is_part ? menus.part_menu() : menus.multi_selection_menu(); } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e3c8895252..d88ae55fad 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -604,11 +604,15 @@ bool Selection::matches(const std::vector& volume_idxs) const bool Selection::requires_uniform_scale() const { #if ENABLE_WORLD_COORDINATE - if (is_single_modifier() || is_single_volume()) + if (is_single_volume_or_modifier()) return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); else if (is_single_full_instance()) +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + return wxGetApp().obj_manipul()->is_world_coordinates() ? +#else return wxGetApp().obj_manipul()->get_world_coordinates() ? - !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()) : false; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()) : false; return true; #else @@ -846,8 +850,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ GLVolume &v = *(*m_volumes)[i]; if (is_single_full_instance()) rotate_instance(v, i); - else if (is_single_volume() || is_single_modifier()) { #if ENABLE_WORLD_COORDINATE + else if (is_single_volume_or_modifier()) { if (transformation_type.local()) v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation); else { @@ -858,7 +862,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ v.set_volume_rotation(Geometry::extract_euler_angles(m)); } #else - if (transformation_type.independent()) + else if (is_single_volume() || is_single_modifier()) { + if (transformation_type.independent()) v.set_volume_rotation(v.get_volume_rotation() + rotation); else { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); @@ -987,7 +992,11 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type #endif // ENABLE_WORLD_COORDINATE } } +#if ENABLE_WORLD_COORDINATE + else if (is_single_volume_or_modifier()) +#else else if (is_single_volume() || is_single_modifier()) +#endif // ENABLE_WORLD_COORDINATE v.set_volume_scaling_factor(scale); else { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); @@ -1335,7 +1344,11 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const if (!boost::starts_with(sidebar_field, "layer")) { const Vec3d center = get_bounding_box().center(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES glsafe(::glTranslated(center.x(), center.y(), center.z())); #if ENABLE_WORLD_COORDINATE Transform3d orient_matrix = Transform3d::Identity(); @@ -1375,10 +1388,18 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const } #endif // ENABLE_WORLD_COORDINATE } +#if ENABLE_WORLD_COORDINATE + else if (is_single_volume_or_modifier()) { +#else else if (is_single_volume() || is_single_modifier()) { +#endif // ENABLE_WORLD_COORDINATE glsafe(::glTranslated(center.x(), center.y(), center.z())); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (!wxGetApp().obj_manipul()->get_world_coordinates()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Transform3d orient_matrix = Transform3d::Identity(); if (boost::starts_with(sidebar_field, "scale")) { const GLVolume* v = (*m_volumes)[*m_list.begin()]; diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 06c5dd23bb..827e6812b6 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -284,6 +284,9 @@ public: bool is_from_single_object() const; bool is_sla_compliant() const; bool is_instance_mode() const { return m_mode == Instance; } +#if ENABLE_WORLD_COORDINATE + bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); } +#endif // ENABLE_WORLD_COORDINATE bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); } // returns true if the selection contains all the given indices From 855bf4acfdb62025df3f9b38dab791b2cb1bb428 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Nov 2021 15:01:24 +0100 Subject: [PATCH 32/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Fixed orientation for sidebar hints in 3D scene for part manipulation in instance and local systems --- src/slic3r/GUI/Plater.cpp | 5 +++++ src/slic3r/GUI/Selection.cpp | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1a2c72abcf..9ae9af2ef3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1445,6 +1445,11 @@ void Sidebar::update_mode() wxWindowUpdateLocker noUpdates(this); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (m_mode == comSimple) + p->object_manipulation->set_coordinates_type(ObjectManipulation::ECoordinatesType::World); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + p->object_list->get_sizer()->Show(m_mode > comSimple); p->object_list->unselect_objects(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d88ae55fad..fa99d5309c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1397,11 +1397,13 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (!wxGetApp().obj_manipul()->is_world_coordinates()) { + Transform3d orient_matrix = Transform3d::Identity(); + if (wxGetApp().obj_manipul()->is_local_coordinates()) { #else if (!wxGetApp().obj_manipul()->get_world_coordinates()) { -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Transform3d orient_matrix = Transform3d::Identity(); if (boost::starts_with(sidebar_field, "scale")) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const GLVolume* v = (*m_volumes)[*m_list.begin()]; orient_matrix = v->get_instance_transformation().get_matrix(true, false, true, true) * v->get_volume_transformation().get_matrix(true, false, true, true); } From 91861d066284599a6983e419803c26a5e3b2f48e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Nov 2021 15:37:43 +0100 Subject: [PATCH 33/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Modified part manipulation fields to show the proper values in dependence of the selected reference system --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6139707e62..a9c69c730e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -689,21 +689,33 @@ void ObjectManipulation::update_settings_value(const Selection& selection) const Vec3d offset = trafo.get_offset() - volume->get_instance_offset(); #else const Vec3d& offset = trafo.get_offset(); - const Vec3d& rotation = trafo.get_rotation(); #endif // ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET // const Vec3d& mirror = trafo.get_mirror(); m_new_position = offset; - m_new_rotation = Vec3d::Zero(); + m_new_rotation = trafo.get_rotation() * (180.0 / M_PI); m_new_size = volume->transformed_convex_hull_bounding_box(trafo.get_matrix()).size(); m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_instance_transformation().get_matrix() * volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0; } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (is_local_coordinates()) { + m_new_position = Vec3d::Zero(); + m_new_rotation = Vec3d::Zero(); + m_new_scale = volume->get_volume_scaling_factor() * 100.0; + m_new_size = volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size()); + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { #endif // ENABLE_WORLD_COORDINATE m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation() * (180.0 / M_PI); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + m_new_size = volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix()).size(); + m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0; +#else m_new_scale = volume->get_volume_scaling_factor() * 100.0; m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #if ENABLE_WORLD_COORDINATE } #endif // ENABLE_WORLD_COORDINATE From c928e17984b9e49ee759c06d1357e6fcbfc5fcbe Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Nov 2021 10:07:11 +0100 Subject: [PATCH 34/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Volumes translation in all reference systems using Move gizmo and part manipulator fields --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_Geometry.cpp | 9 +++ src/slic3r/GUI/GUI_Geometry.hpp | 72 +++++++++++++++++++++++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 12 ++-- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 12 +--- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 29 +++++++-- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 5 +- src/slic3r/GUI/Selection.cpp | 19 ++++++ src/slic3r/GUI/Selection.hpp | 9 +++ 11 files changed, 152 insertions(+), 23 deletions(-) create mode 100644 src/slic3r/GUI/GUI_Geometry.cpp create mode 100644 src/slic3r/GUI/GUI_Geometry.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 9e3d088c48..bf90332301 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -87,7 +87,7 @@ // Enable editing volumes transformation in world coordinates and instances in local coordinates #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_ALPHA4) // Enable showing world coordinates of volumes' offset relative to the instance containing them -#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (1 && ENABLE_WORLD_COORDINATE) +#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (0 && ENABLE_WORLD_COORDINATE) // Enable editing instance coordinates of volumes #define ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES (1 && ENABLE_WORLD_COORDINATE) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9a5252949e..489b8f2705 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -85,6 +85,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_App.hpp GUI/GUI_Utils.cpp GUI/GUI_Utils.hpp + GUI/GUI_Geometry.cpp + GUI/GUI_Geometry.hpp GUI/I18N.cpp GUI/I18N.hpp GUI/MainFrame.cpp diff --git a/src/slic3r/GUI/GUI_Geometry.cpp b/src/slic3r/GUI/GUI_Geometry.cpp new file mode 100644 index 0000000000..b0ed0e04fc --- /dev/null +++ b/src/slic3r/GUI/GUI_Geometry.cpp @@ -0,0 +1,9 @@ +#include "libslic3r/libslic3r.h" +#include "GUI_Geometry.hpp" + +namespace Slic3r { +namespace GUI { + + +} // namespace Slic3r +} // namespace GUI diff --git a/src/slic3r/GUI/GUI_Geometry.hpp b/src/slic3r/GUI/GUI_Geometry.hpp new file mode 100644 index 0000000000..23dd36c396 --- /dev/null +++ b/src/slic3r/GUI/GUI_Geometry.hpp @@ -0,0 +1,72 @@ +#ifndef slic3r_GUI_Geometry_hpp_ +#define slic3r_GUI_Geometry_hpp_ + +namespace Slic3r { +namespace GUI { + +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +enum class ECoordinatesType : unsigned char +{ + World, + Instance, + Local +}; + +class TransformationType +{ +public: + enum Enum { + // Transforming in a world coordinate system + World = 0, + // Transforming in a local coordinate system + Local = 1, + // Absolute transformations, allowed in local coordinate system only. + Absolute = 0, + // Relative transformations, allowed in both local and world coordinate system. + Relative = 2, + // For group selection, the transformation is performed as if the group made a single solid body. + Joint = 0, + // For group selection, the transformation is performed on each object independently. + Independent = 4, + + World_Relative_Joint = World | Relative | Joint, + World_Relative_Independent = World | Relative | Independent, + Local_Absolute_Joint = Local | Absolute | Joint, + Local_Absolute_Independent = Local | Absolute | Independent, + Local_Relative_Joint = Local | Relative | Joint, + Local_Relative_Independent = Local | Relative | Independent, + }; + + TransformationType() : m_value(World) {} + TransformationType(Enum value) : m_value(value) {} + TransformationType& operator=(Enum value) { m_value = value; return *this; } + + Enum operator()() const { return m_value; } + bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } + + void set_world() { this->remove(Local); } + void set_local() { this->add(Local); } + void set_absolute() { this->remove(Relative); } + void set_relative() { this->add(Relative); } + void set_joint() { this->remove(Independent); } + void set_independent() { this->add(Independent); } + + bool world() const { return !this->has(Local); } + bool local() const { return this->has(Local); } + bool absolute() const { return !this->has(Relative); } + bool relative() const { return this->has(Relative); } + bool joint() const { return !this->has(Independent); } + bool independent() const { return this->has(Independent); } + +private: + void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); } + void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); } + + Enum m_value; +}; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + +} // namespace Slic3r +} // namespace GUI + +#endif // slic3r_GUI_Geometry_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index a9c69c730e..b7ba3a2108 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -53,10 +53,10 @@ static choice_ctrl* create_word_local_combo(wxWindow *parent) if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::World)); - temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Instance)); - temp->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Local)); - temp->Select((int)ObjectManipulation::ECoordinatesType::World); + temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::World)); + temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Instance)); + temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Local)); + temp->Select((int)ECoordinatesType::World); #else temp->Append(_L("World coordinates")); temp->Append(_L("Local coordinates")); @@ -978,7 +978,7 @@ wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type) case ECoordinatesType::World: { return _L("World coordinates"); } case ECoordinatesType::Instance: { return _L("Instance coordinates"); } case ECoordinatesType::Local: { return _L("Local coordinates"); } - default: { assert(false); break; } + default: { assert(false); return _L("Unknown"); } } } #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES @@ -1008,7 +1008,7 @@ void ObjectManipulation::change_position_value(int axis, double value) selection.start_dragging(); #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - selection.translate(position - m_cache.position, !is_world_coordinates()); + selection.translate(position - m_cache.position, get_coordinates_type()); #else selection.translate(position - m_cache.position, !m_world_coordinates); #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index d0c38cc477..6a074f3396 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -5,6 +5,9 @@ #include "GUI_ObjectSettings.hpp" #include "GUI_ObjectList.hpp" +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +#include "GUI_Geometry.hpp" +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #include "libslic3r/Point.hpp" #include @@ -72,15 +75,6 @@ public: static const double in_to_mm; static const double mm_to_in; -#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - enum class ECoordinatesType : unsigned char - { - World, - Instance, - Local - }; -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - private: struct Cache { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 88cb841372..d41c938df5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -75,13 +75,20 @@ void GLGizmoMove3D::on_start_dragging() m_displacement = Vec3d::Zero(); #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - if (wxGetApp().obj_manipul()->is_world_coordinates()) + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + const Selection& selection = m_parent.get_selection(); + if (coordinates_type == ECoordinatesType::World) #else if (wxGetApp().obj_manipul()->get_world_coordinates()) #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_starting_drag_position = m_center + m_grabbers[m_hover_id].center; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * Geometry::assemble_transform(Vec3d::Zero(), v.get_volume_rotation()) * m_grabbers[m_hover_id].center; + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { - const Selection& selection = m_parent.get_selection(); const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * m_grabbers[m_hover_id].center; } @@ -320,25 +327,39 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (!wxGetApp().obj_manipul()->is_world_coordinates()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + Transform3d orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true); + if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) + orient_matrix = orient_matrix * v.get_volume_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } #else if (!wxGetApp().obj_manipul()->get_world_coordinates()) { -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES } void GLGizmoMove3D::calc_selection_box_and_center() { const Selection& selection = m_parent.get_selection(); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - if (wxGetApp().obj_manipul()->is_world_coordinates()) { + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + if (coordinates_type == ECoordinatesType::World) { #else if (wxGetApp().obj_manipul()->get_world_coordinates()) { #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_bounding_box = selection.get_bounding_box(); m_center = m_bounding_box.center(); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_bounding_box = v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true)); + m_center = v.world_matrix() * m_bounding_box.center(); + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { m_bounding_box.reset(); const Selection::IndicesList& ids = selection.get_volume_idxs(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 632070485e..7202f1bff7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -622,7 +622,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // Apply new temporary offset #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - selection.translate(get_displacement(), !wxGetApp().obj_manipul()->is_world_coordinates()); + selection.translate(get_displacement(), wxGetApp().obj_manipul()->get_coordinates_type()); #else selection.translate(get_displacement(), !wxGetApp().obj_manipul()->get_world_coordinates()); #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES @@ -652,7 +652,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (control_down) #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->is_world_coordinates()); + selection.translate(get_scale_offset(), wxGetApp().obj_manipul()->get_coordinates_type()); #else selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->get_world_coordinates()); #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9ae9af2ef3..814c7dafad 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -56,6 +56,9 @@ #include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectLayers.hpp" #include "GUI_Utils.hpp" +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +#include "GUI_Geometry.hpp" +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #include "GUI_Factories.hpp" #include "wxExtensions.hpp" #include "MainFrame.hpp" @@ -1447,7 +1450,7 @@ void Sidebar::update_mode() #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (m_mode == comSimple) - p->object_manipulation->set_coordinates_type(ObjectManipulation::ECoordinatesType::World); + p->object_manipulation->set_coordinates_type(ECoordinatesType::World); #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES p->object_list->get_sizer()->Show(m_mode > comSimple); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fa99d5309c..e388c8cbd5 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -714,7 +714,11 @@ void Selection::start_dragging() set_caches(); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +void Selection::translate(const Vec3d& displacement, ECoordinatesType type) +#else void Selection::translate(const Vec3d& displacement, bool local) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES { if (!m_valid) return; @@ -724,8 +728,19 @@ void Selection::translate(const Vec3d& displacement, bool local) for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; if (m_mode == Volume || v.is_wipe_tower) { +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (type == ECoordinatesType::Instance) +#else if (local) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (type == ECoordinatesType::Local) { + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d local_displacement = (volume_data.get_volume_rotation_matrix() * volume_data.get_volume_scale_matrix() * volume_data.get_volume_mirror_matrix()) * displacement; + v.set_volume_offset(volume_data.get_volume_position() + local_displacement); + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { #if ENABLE_WORLD_COORDINATE const VolumeCache& volume_data = m_cache.volumes_data[i]; @@ -740,7 +755,11 @@ void Selection::translate(const Vec3d& displacement, bool local) else if (m_mode == Instance) { #if ENABLE_WORLD_COORDINATE if (is_from_fully_selected_instance(i)) { +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (type == ECoordinatesType::Local) { +#else if (local) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const VolumeCache& volume_data = m_cache.volumes_data[i]; const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; v.set_instance_offset(volume_data.get_instance_position() + world_displacement); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 827e6812b6..b259ba4021 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -2,6 +2,9 @@ #define slic3r_GUI_Selection_hpp_ #include "libslic3r/Geometry.hpp" +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +#include "slic3r/GUI/GUI_Geometry.hpp" +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #include "GLModel.hpp" #include @@ -23,6 +26,7 @@ using ModelObjectPtrs = std::vector; namespace GUI { +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES class TransformationType { public: @@ -75,6 +79,7 @@ private: Enum m_value; }; +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES class Selection { @@ -324,7 +329,11 @@ public: bool is_dragging() const { return m_dragging; } #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + void translate(const Vec3d& displacement, ECoordinatesType type = ECoordinatesType::World); +#else void translate(const Vec3d& displacement, bool local = false); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES void rotate(const Vec3d& rotation, TransformationType transformation_type); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, TransformationType transformation_type); From b7776efdd423c4705e8ba64e3229b3cb07bb595a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Nov 2021 10:31:24 +0100 Subject: [PATCH 35/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Attempt to fix build on non-Windows OSs --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b7ba3a2108..0d2d55b281 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -89,9 +89,9 @@ void msw_rescale_word_local_combo(choice_ctrl* combo) combo->SetSize(size); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::World)); - combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Instance)); - combo->Append(ObjectManipulation::coordinate_type_str(ObjectManipulation::ECoordinatesType::Local)); + combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::World)); + combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Instance)); + combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Local)); #else combo->Append(_L("World coordinates")); combo->Append(_L("Local coordinates")); @@ -515,14 +515,18 @@ void ObjectManipulation::Show(const bool show) bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_volume_or_modifier() && m_word_local_combo->GetCount() < 3) { +#ifdef __linux__ + m_word_local_combo->Insert(coordinate_type_str(ECoordinatesType::Instance), 1); +#else m_word_local_combo->Insert(coordinate_type_str(ECoordinatesType::Instance), wxNullBitmap, 1); +#endif // __linux__ m_word_local_combo->Select((int)ECoordinatesType::World); - this->set_coordinates_type(m_word_local_combo->GetStringSelection()); + this->set_coordinates_type(m_word_local_combo->GetString(m_word_local_combo->GetSelection())); } else if (selection.is_single_full_instance() && m_word_local_combo->GetCount() > 2) { m_word_local_combo->Delete(1); m_word_local_combo->Select((int)ECoordinatesType::World); - this->set_coordinates_type(m_word_local_combo->GetStringSelection()); + this->set_coordinates_type(m_word_local_combo->GetString(m_word_local_combo->GetSelection())); } #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else From 7a263764e10ef838cdc69ccea6dbf281675777e3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Nov 2021 12:46:22 +0100 Subject: [PATCH 36/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Volumes rotation in all reference systems using Rotate gizmo and part manipulator fields --- src/slic3r/GUI/GUI_Geometry.hpp | 20 +++++++++++++------ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 4 ++++ src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 24 ++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 9 ++++++++- src/slic3r/GUI/Selection.cpp | 16 +++++++++++++-- 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GUI_Geometry.hpp b/src/slic3r/GUI/GUI_Geometry.hpp index 23dd36c396..029f01fb51 100644 --- a/src/slic3r/GUI/GUI_Geometry.hpp +++ b/src/slic3r/GUI/GUI_Geometry.hpp @@ -18,19 +18,25 @@ public: enum Enum { // Transforming in a world coordinate system World = 0, + // Transforming in a instance coordinate system + Instance = 1, // Transforming in a local coordinate system - Local = 1, + Local = 2, // Absolute transformations, allowed in local coordinate system only. Absolute = 0, // Relative transformations, allowed in both local and world coordinate system. - Relative = 2, + Relative = 4, // For group selection, the transformation is performed as if the group made a single solid body. Joint = 0, // For group selection, the transformation is performed on each object independently. - Independent = 4, + Independent = 8, World_Relative_Joint = World | Relative | Joint, World_Relative_Independent = World | Relative | Independent, + Instance_Absolute_Joint = Instance | Absolute | Joint, + Instance_Absolute_Independent = Instance | Absolute | Independent, + Instance_Relative_Joint = Instance | Relative | Joint, + Instance_Relative_Independent = Instance | Relative | Independent, Local_Absolute_Joint = Local | Absolute | Joint, Local_Absolute_Independent = Local | Absolute | Independent, Local_Relative_Joint = Local | Relative | Joint, @@ -44,14 +50,16 @@ public: Enum operator()() const { return m_value; } bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } - void set_world() { this->remove(Local); } - void set_local() { this->add(Local); } + void set_world() { this->remove(Instance); this->remove(Local); } + void set_instance() { this->remove(Local); this->add(Instance); } + void set_local() { this->remove(Instance); this->add(Local); } void set_absolute() { this->remove(Relative); } void set_relative() { this->add(Relative); } void set_joint() { this->remove(Independent); } void set_independent() { this->add(Independent); } - bool world() const { return !this->has(Local); } + bool world() const { return !this->has(Instance) && !this->has(Local); } + bool instance() const { return this->has(Instance); } bool local() const { return this->has(Local); } bool absolute() const { return !this->has(Relative); } bool relative() const { return this->has(Relative); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 0d2d55b281..acaa8d2b33 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -842,7 +842,11 @@ void ObjectManipulation::update_reset_buttons_visibility() bool show_drop_to_bed = false; #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (m_coordinates_type != ECoordinatesType::Local && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier())) { +#else if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Vec3d rotation = Vec3d::Zero(); Vec3d scale = Vec3d::Ones(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 4d82f5b9cf..c6fb13e14b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -207,23 +207,32 @@ void GLGizmoRotate::on_render_for_picking() void GLGizmoRotate::init_data_from_selection(const Selection& selection) { #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - if (wxGetApp().obj_manipul()->is_world_coordinates()) { + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + if (coordinates_type == ECoordinatesType::World) { #else if (wxGetApp().obj_manipul()->get_world_coordinates()) { #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_bounding_box = selection.get_bounding_box(); m_center = m_bounding_box.center(); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_bounding_box = v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true)); + m_center = v.world_matrix() * m_bounding_box.center(); + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { m_bounding_box.reset(); const Selection::IndicesList& ids = selection.get_volume_idxs(); for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - m_bounding_box.merge(v->transformed_convex_hull_bounding_box(v->get_volume_transformation().get_matrix())); + const GLVolume& v = *selection.get_volume(id); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); } m_bounding_box = m_bounding_box.transformed(selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(true, true, false, true)); m_center = selection.get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center(); } + m_radius = Offset + m_bounding_box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; @@ -231,13 +240,18 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - if (wxGetApp().obj_manipul()->is_world_coordinates()) + if (coordinates_type == ECoordinatesType::World) + m_orient_matrix = Transform3d::Identity(); + else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true) * v.get_volume_transformation().get_matrix(true, false, true, true); +} #else if (wxGetApp().obj_manipul()->get_world_coordinates()) -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_orient_matrix = Transform3d::Identity(); else m_orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES } #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 7202f1bff7..51d523d0f6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -667,7 +667,14 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // Apply new temporary rotations #if ENABLE_WORLD_COORDINATE #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - TransformationType transformation_type = wxGetApp().obj_manipul()->is_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint; + TransformationType transformation_type; + switch (wxGetApp().obj_manipul()->get_coordinates_type()) + { + default: + case ECoordinatesType::World: { transformation_type = TransformationType::World_Relative_Joint; break; } + case ECoordinatesType::Instance: { transformation_type = TransformationType::Instance_Relative_Joint; break; } + case ECoordinatesType::Local: { transformation_type = TransformationType::Local_Relative_Joint; break; } + } #else TransformationType transformation_type(wxGetApp().obj_manipul()->get_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint); #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e388c8cbd5..fc0a89d895 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -845,10 +845,11 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ const Vec3d new_rotation = transformation_type.world() ? Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()) : transformation_type.absolute() ? rotation : Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m); - if (rot_axis_max == 2 && transformation_type.joint()) { + const Vec3d relative_instance_offset = m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center; + if (rot_axis_max == 2 && transformation_type.joint() && !relative_instance_offset.isApprox(Vec3d::Zero())) { // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation); - volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * relative_instance_offset); } #else const Vec3d new_rotation = transformation_type.world() ? @@ -871,8 +872,19 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ rotate_instance(v, i); #if ENABLE_WORLD_COORDINATE else if (is_single_volume_or_modifier()) { +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (transformation_type.local()) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + v.set_volume_rotation(Geometry::extract_euler_angles(m_cache.volumes_data[i].get_volume_rotation_matrix() * m)); + } + else if (transformation_type.instance()) { + const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + v.set_volume_rotation(Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix())); + } +#else if (transformation_type.local()) v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); m = m * m_cache.volumes_data[i].get_instance_rotation_matrix(); From a1079b14dcd0790d4f4fbf40490d1ad99a65e342 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 11 Nov 2021 15:25:37 +0100 Subject: [PATCH 37/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Volumes scaling in all reference systems using Scale gizmo and part manipulator fields --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 25 +++++++++ src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 55 +++++++++++++++++--- src/slic3r/GUI/Selection.cpp | 62 +++++++++++++++++++++-- src/slic3r/GUI/Selection.hpp | 12 +++++ 5 files changed, 144 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 0f8a41ff21..6711b99976 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -788,10 +788,35 @@ void ObjectManipulation::update_if_dirty() update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + Selection::EUniformScaleRequiredReason reason; + if (selection.requires_uniform_scale(&reason)) { +#else if (selection.requires_uniform_scale()) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES m_lock_bnt->SetLock(true); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + wxString tooltip; + if (selection.is_single_volume_or_modifier()) { + if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance) + tooltip = _L("You cannot use non-uniform scaling mode for parts non aligned with the instance local axes"); + else if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_World) + tooltip = _L("You cannot use non-uniform scaling mode for parts non aligned with the printer axes"); + } + else if (selection.is_single_full_instance()) { + if (reason == Selection::EUniformScaleRequiredReason::InstanceNotAxisAligned_World) + tooltip = _L("You cannot use non-uniform scaling mode for instances non aligned with the printer axes"); + else if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance) + tooltip = _L("You cannot use non-uniform scaling mode for instances containing non locally axis-aligned parts"); + } + else + tooltip = _L("You cannot use non-uniform scaling mode for multiple objects/parts selection"); + + m_lock_bnt->SetToolTip(tooltip); +#else m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection or non axis-aligned objects/parts")); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection")); #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index d41c938df5..e17ac93cdd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -74,9 +74,9 @@ void GLGizmoMove3D::on_start_dragging() if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); #if ENABLE_WORLD_COORDINATE + const Selection& selection = m_parent.get_selection(); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); - const Selection& selection = m_parent.get_selection(); if (coordinates_type == ECoordinatesType::World) #else if (wxGetApp().obj_manipul()->get_world_coordinates()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index f62ed9320d..1497c36ccb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -136,11 +136,11 @@ void GLGizmoScale3D::on_render() m_center = Vec3d::Zero(); m_instance_center = Vec3d::Zero(); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); + if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) { #else bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_full_instance() && !world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else m_transform = Transform3d::Identity(); // Transforms grabbers' offsets to world refefence system @@ -182,19 +182,39 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_WORLD_COORDINATE } #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates()) { +#else else if (selection.is_single_volume_or_modifier() && !world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else else if (selection.is_single_modifier() || selection.is_single_volume()) { #endif // ENABLE_WORLD_COORDINATE const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, false, false, true))); + Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true, true)); +#else m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true))); Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true) * v.get_volume_transformation().get_matrix(true, false, true)); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES trafo.set_offset(v.world_matrix().translation()); m_grabbers_transform = trafo.get_matrix(); m_center = v.world_matrix() * m_bounding_box.center(); m_instance_center = m_center; } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true))); + Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true, true) * v.get_volume_transformation().get_matrix(true, false, true, true)); + trafo.set_offset(v.world_matrix().translation()); + m_grabbers_transform = trafo.get_matrix(); + m_center = v.world_matrix() * m_bounding_box.center(); + m_instance_center = m_center; + } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else { m_bounding_box = selection.get_bounding_box(); m_grabbers_transform = Geometry::assemble_transform(m_bounding_box.center()); @@ -422,11 +442,12 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) Vec3d starting_scale = m_starting.scale; const Selection& selection = m_parent.get_selection(); #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - const bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + if (coordinates_type == ECoordinatesType::World) { #else const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_full_instance()) { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()); curr_scale = (m * curr_scale).cwiseAbs(); @@ -443,7 +464,11 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) curr_scale(axis) = starting_scale(axis) * ratio; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (coordinates_type == ECoordinatesType::World) { +#else if (world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_full_instance()) m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs(); else if (selection.is_single_volume_or_modifier()) { @@ -473,7 +498,11 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) #if ENABLE_WORLD_COORDINATE Vec3d center_offset = m_starting.instance_center - m_starting.center; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (selection.is_single_full_instance() && coordinates_type != ECoordinatesType::World) { +#else if (selection.is_single_full_instance() && !world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); center_offset = m * center_offset; } @@ -488,11 +517,13 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: { m_offset = Vec3d::Zero(); break; } } +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_volume_or_modifier() && !world_coordinates) { const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); m_offset = mv * mi * m_offset; } +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES #else Vec3d local_offset_vec; switch (axis) @@ -526,25 +557,29 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) m_offset.y() *= -1.0; const Selection& selection = m_parent.get_selection(); -#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - const bool world_coordinates = wxGetApp().obj_manipul()->is_world_coordinates(); -#else +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates(); -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Vec3d center_offset = m_starting.instance_center - m_starting.center; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) { +#else if (selection.is_single_full_instance() && !world_coordinates) { +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse(); center_offset = m * center_offset; } m_offset += (ratio - 1.0) * center_offset; +#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (selection.is_single_volume_or_modifier() && !world_coordinates) { const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()); const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse(); m_offset = mv * mi * m_offset; } +#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES } else #endif // ENABLE_WORLD_COORDINATE @@ -598,7 +633,11 @@ void GLGizmoScale3D::transform_to_local(const Selection& selection) const if (!wxGetApp().obj_manipul()->get_world_coordinates()) { #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) +#else if (selection.is_single_volume_or_modifier()) +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES orient_matrix = orient_matrix * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_transformation().get_matrix(true, false, true, true); glsafe(::glMultMatrixd(orient_matrix.data())); } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fc0a89d895..04f4969258 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -601,18 +601,74 @@ bool Selection::matches(const std::vector& volume_idxs) const return count == (unsigned int)m_list.size(); } +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES +bool Selection::requires_uniform_scale(EUniformScaleRequiredReason* reason) const +#else bool Selection::requires_uniform_scale() const +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES { #if ENABLE_WORLD_COORDINATE +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + ECoordinatesType coord_type = wxGetApp().obj_manipul()->get_coordinates_type(); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (is_single_volume_or_modifier()) +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + { + if (coord_type == ECoordinatesType::World) { + if (!Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation())) { + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::VolumeNotAxisAligned_World; + return true; + } + } + else if (coord_type == ECoordinatesType::Instance) { + if (!Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_volume_rotation())) { + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance; + return true; + } + } + return false; + } +#else return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation()); +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else if (is_single_full_instance()) #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - return wxGetApp().obj_manipul()->is_world_coordinates() ? + if (coord_type == ECoordinatesType::World) { + if (!Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation())) { + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::InstanceNotAxisAligned_World; + return true; + } + else { + for (unsigned int i : m_list) { + if (!Geometry::is_rotation_ninety_degrees((*m_volumes)[i]->get_volume_rotation())) { + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance; + return true; + } + } + } + return false; + } + else { + for (unsigned int i : m_list) { + if (!Geometry::is_rotation_ninety_degrees((*m_volumes)[i]->get_volume_rotation())) { + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance; + return true; + } + } + return false; + } + + if (reason != nullptr) + *reason = EUniformScaleRequiredReason::MultipleSelection; #else return wxGetApp().obj_manipul()->get_world_coordinates() ? -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES !Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()) : false; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES return true; #else @@ -737,7 +793,7 @@ void Selection::translate(const Vec3d& displacement, bool local) #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else if (type == ECoordinatesType::Local) { const VolumeCache& volume_data = m_cache.volumes_data[i]; - const Vec3d local_displacement = (volume_data.get_volume_rotation_matrix() * volume_data.get_volume_scale_matrix() * volume_data.get_volume_mirror_matrix()) * displacement; + const Vec3d local_displacement = (volume_data.get_volume_rotation_matrix() * volume_data.get_volume_mirror_matrix()) * displacement; v.set_volume_offset(volume_data.get_volume_position() + local_displacement); } #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index b259ba4021..2eaa34fa66 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -301,7 +301,19 @@ public: // returns true if the selection contains all and only the given indices bool matches(const std::vector& volume_idxs) const; +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + enum class EUniformScaleRequiredReason : unsigned char + { + NotRequired, + InstanceNotAxisAligned_World, + VolumeNotAxisAligned_World, + VolumeNotAxisAligned_Instance, + MultipleSelection, + }; + bool requires_uniform_scale(EUniformScaleRequiredReason* reason = nullptr) const; +#else bool requires_uniform_scale() const; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES // Returns the the object id if the selection is from a single object, otherwise is -1 int get_object_idx() const; From f69e557e200d6c1133141baca5a53fed966c3bb0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 15 Nov 2021 13:00:12 +0100 Subject: [PATCH 38/50] Tech ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - Mirror transform in local system for volumes and a few fixes in rotation --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 6 +++++- src/slic3r/GUI/Selection.cpp | 10 ++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6711b99976..851364381e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -930,7 +930,7 @@ void ObjectManipulation::update_mirror_buttons_visibility() std::array new_states = {mbHidden, mbHidden, mbHidden}; #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES - if (!is_world_coordinates()) { + if (is_local_coordinates()) { #else if (!m_world_coordinates) { #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 166bfa402b..cede4c7a3a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -245,7 +245,11 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); m_orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true) * v.get_volume_transformation().get_matrix(true, false, true, true); -} + } + else { + const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin()); + m_orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true); + } #else if (wxGetApp().obj_manipul()->get_world_coordinates()) m_orient_matrix = Transform3d::Identity(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 04f4969258..2ff9d9d523 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -793,7 +793,7 @@ void Selection::translate(const Vec3d& displacement, bool local) #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES else if (type == ECoordinatesType::Local) { const VolumeCache& volume_data = m_cache.volumes_data[i]; - const Vec3d local_displacement = (volume_data.get_volume_rotation_matrix() * volume_data.get_volume_mirror_matrix()) * displacement; + const Vec3d local_displacement = volume_data.get_volume_rotation_matrix() * displacement; v.set_volume_offset(volume_data.get_volume_position() + local_displacement); } #endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES @@ -813,11 +813,13 @@ void Selection::translate(const Vec3d& displacement, bool local) if (is_from_fully_selected_instance(i)) { #if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES if (type == ECoordinatesType::Local) { + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d world_displacement = volume_data.get_instance_rotation_matrix() * displacement; #else if (local) { -#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES const VolumeCache& volume_data = m_cache.volumes_data[i]; const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES v.set_instance_offset(volume_data.get_instance_position() + world_displacement); } else @@ -1439,6 +1441,9 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const glsafe(::glTranslated(center.x(), center.y(), center.z())); #if ENABLE_WORLD_COORDINATE Transform3d orient_matrix = Transform3d::Identity(); +#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES + orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); +#else if (boost::starts_with(sidebar_field, "position") || boost::starts_with(sidebar_field, "scale")) orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); else if (boost::starts_with(sidebar_field, "rotation")) { @@ -1452,6 +1457,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const orient_matrix.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ())); } } +#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES glsafe(::glMultMatrixd(orient_matrix.data())); #else From f03f8f52d7ebfee528c9c1894354d09ce9c98177 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 1 Dec 2021 15:44:44 +0100 Subject: [PATCH 39/50] Fixed loading translation of hint notification text and reloading this texts in other langs. --- src/slic3r/GUI/HintNotification.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 450067c48a..fc61ab62d8 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -294,6 +294,10 @@ void HintDatabase::uninit() write_used_binary(m_used_ids); } m_initialized = false; + m_loaded_hints.clear(); + m_sorted_hints = false; + m_used_ids.clear(); + m_used_ids_loaded = false; } void HintDatabase::init() { @@ -336,7 +340,8 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) size_t weight = 1; bool was_displayed = is_used(id_string); //unescape text1 - unescape_string_cstyle(_utf8(dict["text"]), fulltext); + unescape_string_cstyle(dict["text"], fulltext); + fulltext = _utf8(fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); std::string marker_e(1, ImGui::ColorMarkerEnd); From e8697d2fc29ce017c94f7fc1cc1b8922d0bfc298 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 1 Dec 2021 16:44:51 +0100 Subject: [PATCH 40/50] WIP Cura Lightning infill, initial port. --- src/libslic3r/CMakeLists.txt | 8 + src/libslic3r/Fill/FillRectilinear.cpp | 37 +- src/libslic3r/Fill/FillRectilinear.hpp | 4 + .../Fill/Lightning/DistanceField.cpp | 98 +++++ .../Fill/Lightning/DistanceField.hpp | 97 +++++ src/libslic3r/Fill/Lightning/Generator.cpp | 125 ++++++ src/libslic3r/Fill/Lightning/Generator.hpp | 126 ++++++ src/libslic3r/Fill/Lightning/Layer.cpp | 410 ++++++++++++++++++ src/libslic3r/Fill/Lightning/Layer.hpp | 87 ++++ src/libslic3r/Fill/Lightning/TreeNode.cpp | 408 +++++++++++++++++ src/libslic3r/Fill/Lightning/TreeNode.hpp | 275 ++++++++++++ src/libslic3r/Point.hpp | 2 +- 12 files changed, 1675 insertions(+), 2 deletions(-) create mode 100644 src/libslic3r/Fill/Lightning/DistanceField.cpp create mode 100644 src/libslic3r/Fill/Lightning/DistanceField.hpp create mode 100644 src/libslic3r/Fill/Lightning/Generator.cpp create mode 100644 src/libslic3r/Fill/Lightning/Generator.hpp create mode 100644 src/libslic3r/Fill/Lightning/Layer.cpp create mode 100644 src/libslic3r/Fill/Lightning/Layer.hpp create mode 100644 src/libslic3r/Fill/Lightning/TreeNode.cpp create mode 100644 src/libslic3r/Fill/Lightning/TreeNode.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 96d31bf3f7..6d5f18fc6e 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -69,6 +69,14 @@ add_library(libslic3r STATIC Fill/FillLine.hpp Fill/FillRectilinear.cpp Fill/FillRectilinear.hpp + Fill/Lightning/DistanceField.cpp + Fill/Lightning/DistanceField.hpp + Fill/Lightning/Generator.cpp + Fill/Lightning/Generator.hpp + Fill/Lightning/Layer.cpp + Fill/Lightning/Layer.hpp + Fill/Lightning/TreeNode.cpp + Fill/Lightning/TreeNode.hpp Flow.cpp Flow.hpp format.hpp diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index fc0c1b30c7..69476175e0 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -3041,5 +3041,40 @@ Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams return polylines_out; } -} // namespace Slic3r +Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing) +{ + ExPolygonWithOffset poly_with_offset(expolygon, 0, 0, 0); + BoundingBox bounding_box = poly_with_offset.bounding_box_src(); + std::vector segs = slice_region_by_vertical_lines( + poly_with_offset, + (bounding_box.max.x() - bounding_box.min.x() + spacing - 1) / spacing, + bounding_box.min.x(), + spacing); + Points out; + for (const SegmentedIntersectionLine &sil : segs) { + for (size_t i = 0; i < sil.intersections.size(); i += 2) { + coord_t a = sil.intersections[i].pos(); + coord_t b = sil.intersections[i + 1].pos(); + for (coord_t y = a - (a % spacing) - spacing; y < b; y += spacing) + if (y > a) + out.emplace_back(sil.pos, y); + } + } + return out; +} + +Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing) +{ + Points out; + for (const ExPolygon &expoly : expolygons) + append(out, sample_grid_pattern(expoly, spacing)); + return out; +} + +Points sample_grid_pattern(const Polygons &polygons, coord_t spacing) +{ + return sample_grid_pattern(union_ex(polygons), spacing); +} + +} // namespace Slic3r diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp index ad32ad20f3..ba735dd02c 100644 --- a/src/libslic3r/Fill/FillRectilinear.hpp +++ b/src/libslic3r/Fill/FillRectilinear.hpp @@ -109,6 +109,10 @@ protected: float _layer_angle(size_t idx) const override { return 0.f; } }; +Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing); +Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing); +Points sample_grid_pattern(const Polygons &polygons, coord_t spacing); + } // namespace Slic3r #endif // slic3r_FillRectilinear_hpp_ diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp new file mode 100644 index 0000000000..0a731915dd --- /dev/null +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -0,0 +1,98 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "DistanceField.hpp" //Class we're implementing. +#include "../FillRectilinear.hpp" + +namespace Slic3r +{ + +constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient. + +LightningDistanceField::LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : + m_cell_size(radius / radius_per_cell_size), + m_supporting_radius(radius) +{ + m_supporting_radius2 = double(radius) * double(radius); + // Sample source polygons with a regular grid sampling pattern. + for (const ExPolygon &expoly : union_ex(current_outline)) { + for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) { + // Find a squared distance to the source expolygon boundary. + double d2 = std::numeric_limits::max(); + for (size_t icontour = 0; icontour <= expoly.holes.size(); ++ icontour) { + const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; + if (contour.size() > 2) { + Point prev = contour.points.back(); + for (const Point &p2 : contour.points) { + d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); + prev = p2; + } + } + } + m_unsupported_points.emplace_back(p, sqrt(d2)); + } + } + m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) { + constexpr coord_t prime_for_hash = 191; + return std::abs(b.dist_to_boundary - a.dist_to_boundary) > radius ? + a.dist_to_boundary < b.dist_to_boundary : + (PointHash{}(a.loc) % prime_for_hash) < (PointHash{}(b.loc) % prime_for_hash); + }); + for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) { + UnsupportedCell& cell = *it; + m_unsupported_points_grid.emplace(Point{ cell.loc.x() / m_cell_size, cell.loc.y() / m_cell_size }, it); + } +} + +void LightningDistanceField::update(const Point& to_node, const Point& added_leaf) +{ + Vec2d v = (added_leaf - to_node).cast(); + auto l2 = v.squaredNorm(); + Vec2d extent = Vec2d(-v.y(), v.x()) * m_supporting_radius / sqrt(l2); + + BoundingBox grid; + { + Point diagonal(m_supporting_radius, m_supporting_radius); + Point iextent(extent.cast()); + grid = BoundingBox(added_leaf - diagonal, added_leaf + diagonal); + grid.merge(to_node - iextent); + grid.merge(to_node + iextent); + grid.merge(added_leaf - iextent); + grid.merge(added_leaf + iextent); + grid.min /= m_cell_size; + grid.max /= m_cell_size; + } + + Point grid_loc; + for (coord_t row = grid.min.y(); row <= grid.max.y(); ++ row) { + grid_loc.y() = row * m_cell_size; + for (coord_t col = grid.min.x(); col <= grid.max.y(); ++ col) { + grid_loc.x() = col * m_cell_size; + // Test inside a circle at the new leaf. + if ((grid_loc - added_leaf).cast().squaredNorm() > m_supporting_radius2) { + // Not inside a circle at the end of the new leaf. + // Test inside a rotated rectangle. + Vec2d vx = (grid_loc - to_node).cast(); + double d = v.dot(vx); + if (d >= 0 && d <= l2) { + d = extent.dot(vx); + if (d < -1. || d > 1.) + // Not inside a rotated rectangle. + continue; + } + } + // Inside a circle at the end of the new leaf, or inside a rotated rectangle. + // Remove unsupported leafs at this grid location. + if (auto it = m_unsupported_points_grid.find(grid_loc); it != m_unsupported_points_grid.end()) { + std::list::iterator& list_it = it->second; + UnsupportedCell& cell = *list_it; + if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { + m_unsupported_points.erase(list_it); + m_unsupported_points_grid.erase(it); + } + } + } + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp new file mode 100644 index 0000000000..9d761a03ba --- /dev/null +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -0,0 +1,97 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef LIGHTNING_DISTANCE_FIELD_H +#define LIGHTNING_DISTANCE_FIELD_H + +namespace Slic3r +{ + +/*! + * 2D field that maintains locations which need to be supported for Lightning + * Infill. + * + * This field contains a set of "cells", spaced out in a grid. Each cell + * maintains how far it is removed from the edge, which is used to determine + * how it gets supported by Lightning Infill. + */ +class LightningDistanceField +{ +public: + /*! + * Construct a new field to calculate Lightning Infill with. + * \param radius The radius of influence that an infill line is expected to + * support in the layer above. + * \param current_outline The total infill area on this layer. + * \param current_overhang The overhang that needs to be supported on this + * layer. + */ + LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); + + /*! + * Gets the next unsupported location to be supported by a new branch. + * \param p Output variable for the next point to support. + * \return ``true`` if successful, or ``false`` if there are no more points + * to consider. + */ + bool tryGetNextPoint(Point* p) const { + if (m_unsupported_points.empty()) + return false; + *p = m_unsupported_points.front().loc; + return true; + } + + /*! + * Update the distance field with a newly added branch. + * + * The branch is a line extending from \p to_node to \p added_leaf . This + * function updates the grid cells so that the distance field knows how far + * off it is from being supported by the current pattern. Grid points are + * updated with sampling points spaced out by the supporting radius along + * the line. + * \param to_node The node endpoint of the newly added branch. + * \param added_leaf The location of the leaf of the newly added branch, + * drawing a straight line to the node. + */ + void update(const Point& to_node, const Point& added_leaf); + +protected: + /*! + * Spacing between grid points to consider supporting. + */ + coord_t m_cell_size; + + /*! + * The radius of the area of the layer above supported by a point on a + * branch of a tree. + */ + coord_t m_supporting_radius; + double m_supporting_radius2; + + /*! + * Represents a small discrete area of infill that needs to be supported. + */ + struct UnsupportedCell + { + UnsupportedCell(Point grid_loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} + // The position of the center of this cell. + Point loc; + // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. + coord_t dist_to_boundary; + }; + + /*! + * Cells which still need to be supported at some point. + */ + std::list m_unsupported_points; + + /*! + * Links the unsupported points to a grid point, so that we can quickly look + * up the cell belonging to a certain position in the grid. + */ + std::unordered_map::iterator, PointHash> m_unsupported_points_grid; +}; + +} // namespace Slic3r + +#endif //LIGHTNING_DISTANCE_FIELD_H diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp new file mode 100644 index 0000000000..dfe0d9e35b --- /dev/null +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -0,0 +1,125 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "Generator.hpp" +#include "TreeNode.hpp" + +#include "../../ClipperUtils.hpp" +#include "../../Layer.hpp" +#include "../../Print.hpp" +#include "../../Surface.hpp" + +/* Possible future tasks/optimizations,etc.: + * - Improve connecting heuristic to favor connecting to shorter trees + * - Change which node of a tree is the root when that would be better in reconnectRoots. + * - (For implementation in Infill classes & elsewhere): Outline offset, infill-overlap & perimeter gaps. + * - Allow for polylines, i.e. merge Tims PR about polyline fixes + * - Unit Tests? + * - Optimization: let the square grid store the closest point on boundary + * - Optimization: only compute the closest dist to / point on boundary for the outer cells and flood-fill the rest + * - Make a pass with Arachne over the output. Somehow. + * - Generate all to-be-supported points at once instead of sequentially: See branch interlocking_gen PolygonUtils::spreadDots (Or work with sparse grids.) + * - Lots of magic values ... to many to parameterize. But are they the best? + * - Move more complex computations from LightningGenerator constructor to elsewhere. + */ + +using namespace Slic3r; + +LightningGenerator::LightningGenerator(const PrintObject &print_object) +{ + const PrintConfig &print_config = print_object.print()->config(); + const PrintObjectConfig &object_config = print_object.config(); + const PrintRegionConfig ®ion_config = print_object.shared_regions()->all_regions.front()->config(); + const std::vector &nozzle_diameters = print_config.nozzle_diameter.values; + double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); + const int infill_extruder = region_config.infill_extruder.value; + const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); + // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. + const double layer_thickness = object_config.layer_height; + + m_infill_extrusion_width = scaled(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width : region_config.infill_extrusion_width); + m_supporting_radius = scaled(m_infill_extrusion_width * 0.001 / region_config.fill_density); + + const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees + const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees + const double lightning_infill_straightening_angle = M_PI / 4; // 45 degrees + m_wall_supporting_radius = layer_thickness * std::tan(lightning_infill_overhang_angle); + m_prune_length = layer_thickness * std::tan(lightning_infill_prune_angle); + m_straightening_max_distance = layer_thickness * std::tan(lightning_infill_straightening_angle); + + generateInitialInternalOverhangs(print_object); + generateTrees(print_object); +} + +void LightningGenerator::generateInitialInternalOverhangs(const PrintObject &print_object) +{ + m_overhang_per_layer.resize(print_object.layers().size()); + const float infill_wall_offset = - m_infill_extrusion_width; + + Polygons infill_area_above; + //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. + for (int layer_nr = print_object.layers().size() - 1; layer_nr >= 0; layer_nr--) { + Polygons infill_area_here; + for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) + for (const Surface& surface : layerm->fill_surfaces.surfaces) + if (surface.surface_type == stInternal) + append(infill_area_here, offset(surface.expolygon, infill_wall_offset)); + + //Remove the part of the infill area that is already supported by the walls. + Polygons overhang = diff(offset(infill_area_here, -m_wall_supporting_radius), infill_area_above); + + m_overhang_per_layer[layer_nr] = overhang; + infill_area_above = std::move(infill_area_here); + } +} + +const LightningLayer& LightningGenerator::getTreesForLayer(const size_t& layer_id) const +{ + assert(layer_id < m_lightning_layers.size()); + return m_lightning_layers[layer_id]; +} + +void LightningGenerator::generateTrees(const PrintObject &print_object) +{ + m_lightning_layers.resize(print_object.layers().size()); + const coord_t infill_wall_offset = - m_infill_extrusion_width; + + std::vector infill_outlines(print_object.layers().size(), Polygons()); + + // For-each layer from top to bottom: + for (int layer_id = print_object.layers().size() - 1; layer_id >= 0; layer_id--) + for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) + for (const Surface &surface : layerm->fill_surfaces.surfaces) + if (surface.surface_type == stInternal) + append(infill_outlines[layer_id], offset(surface.expolygon, infill_wall_offset)); + + // For various operations its beneficial to quickly locate nearby features on the polygon: + const size_t top_layer_id = print_object.layers().size() - 1; + EdgeGrid::Grid outlines_locator(get_extents(infill_outlines[top_layer_id]).inflated(SCALED_EPSILON)); + outlines_locator.create(infill_outlines[top_layer_id], locator_cell_size); + + // For-each layer from top to bottom: + for (int layer_id = top_layer_id; layer_id >= 0; layer_id--) + { + LightningLayer& current_lightning_layer = m_lightning_layers[layer_id]; + Polygons& current_outlines = infill_outlines[layer_id]; + + // register all trees propagated from the previous layer as to-be-reconnected + std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; + + current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + + // Initialize trees for next lower layer from the current one. + if (layer_id == 0) + return; + + const Polygons& below_outlines = infill_outlines[layer_id - 1]; + outlines_locator.set_bbox(get_extents(below_outlines).inflated(SCALED_EPSILON)); + outlines_locator.create(below_outlines, locator_cell_size); + + std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; + for (auto& tree : current_lightning_layer.tree_roots) + tree->propagateToNextLayer(lower_trees, below_outlines, outlines_locator, m_prune_length, m_straightening_max_distance, locator_cell_size / 2); + } +} diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp new file mode 100644 index 0000000000..7de05d2031 --- /dev/null +++ b/src/libslic3r/Fill/Lightning/Generator.hpp @@ -0,0 +1,126 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef LIGHTNING_GENERATOR_H +#define LIGHTNING_GENERATOR_H + +#include "Layer.hpp" + +#include +#include +#include + +namespace Slic3r +{ +class PrintObject; + +/*! + * Generates the Lightning Infill pattern. + * + * The lightning infill pattern is designed to use a minimal amount of material + * to support the top skin of the print, while still printing with reasonably + * consistently flowing lines. It sacrifices strength completely in favour of + * top surface quality and reduced print time / material usage. + * + * Lightning Infill is so named because the patterns it creates resemble a + * forked path with one main path and many small lines on the side. These paths + * grow out from the sides of the model just below where the top surface needs + * to be supported from the inside, so that minimal material is needed. + * + * This pattern is based on a paper called "Ribbed Support Vaults for 3D + * Printing of Hollowed Objects" by Tricard, Claux and Lefebvre: + * https://www.researchgate.net/publication/333808588_Ribbed_Support_Vaults_for_3D_Printing_of_Hollowed_Objects + */ +class LightningGenerator // "Just like Nicola used to make!" +{ +public: + /*! + * Create a generator to fill a certain mesh with infill. + * + * This generator will pre-compute things in preparation of generating + * Lightning Infill for the infill areas in that mesh. The infill areas must + * already be calculated at this point. + * \param mesh The mesh to generate infill for. + */ + LightningGenerator(const PrintObject &print_object); + + /*! + * Get a tree of paths generated for a certain layer of the mesh. + * + * This tree represents the paths that must be traced to print the infill. + * \param layer_id The layer number to get the path tree for. This is within + * the range of layers of the mesh (not the global layer numbers). + * \return A tree structure representing paths to print to create the + * Lightning Infill pattern. + */ + const LightningLayer& getTreesForLayer(const size_t& layer_id) const; + +protected: + /*! + * Calculate the overhangs above the infill areas that need to be supported + * by infill. + * + * Normally, overhangs are only generated for the outside of the model and + * only when support is generated. For this pattern, we also need to + * generate overhang areas for the inside of the model. + */ + void generateInitialInternalOverhangs(const PrintObject &print_object); + + /*! + * Calculate the tree structure of all layers. + */ + void generateTrees(const PrintObject &print_object); + + float m_infill_extrusion_width; + + /*! + * How far each piece of infill can support skin in the layer above. + */ + coord_t m_supporting_radius; + + /*! + * How far a wall can support the wall above it. If a wall completely + * supports the wall above it, no infill needs to support that. + * + * This is similar to the overhang distance calculated for support. It is + * determined by the lightning_infill_overhang_angle setting. + */ + coord_t m_wall_supporting_radius; + + /*! + * How far each piece of infill can support other infill in the layer above. + * + * This may be different than \ref supporting_radius, because the infill is + * printed with one end floating in mid-air. This endpoint will sag more, so + * an infill line may need to be supported more than a skin line. + */ + coord_t m_prune_length; + + /*! + * How far a line may be shifted in order to straighten the line out. + * + * Straightening the line reduces material and time usage and reduces + * accelerations needed to print the pattern. However it makes the infill + * weak if lines are partially suspended next to the line on the previous + * layer. + */ + coord_t m_straightening_max_distance; + + /*! + * For each layer, the overhang that needs to be supported by the pattern. + * + * This is generated by \ref generateInitialInternalOverhangs. + */ + std::vector m_overhang_per_layer; + + /*! + * For each layer, the generated lightning paths. + * + * This is generated by \ref generateTrees. + */ + std::vector m_lightning_layers; +}; + +} // namespace Slic3r + +#endif // LIGHTNING_GENERATOR_H diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp new file mode 100644 index 0000000000..62f1617f9a --- /dev/null +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -0,0 +1,410 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "Layer.hpp" //The class we're implementing. + +#include // advance + +#include "DistanceField.hpp" +#include "TreeNode.hpp" + +#include "../../Geometry.hpp" + +using namespace Slic3r; + +coord_t LightningLayer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location) +{ + return coord_t((boundary_loc - unsupported_location).cast().norm()); +} + +Point GroundingLocation::p() const +{ + assert(tree_node || boundary_location); + return tree_node ? tree_node->getLocation() : *boundary_location; +} + +void LightningLayer::fillLocator(SparseLightningTreeNodeGrid &tree_node_locator) +{ + std::function add_node_to_locator_func = [&tree_node_locator](LightningTreeNodeSPtr node) { + tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node)); + }; + for (auto& tree : tree_roots) + tree->visitNodes(add_node_to_locator_func); +} + +void LightningLayer::generateNewTrees +( + const Polygons& current_overhang, + const Polygons& current_outlines, + const EdgeGrid::Grid& outlines_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius +) +{ + LightningDistanceField distance_field(supporting_radius, current_outlines, current_overhang); + + SparseLightningTreeNodeGrid tree_node_locator; + fillLocator(tree_node_locator); + + // Until no more points need to be added to support all: + // Determine next point from tree/outline areas via distance-field + Point unsupported_location; + while (distance_field.tryGetNextPoint(&unsupported_location)) { + GroundingLocation grounding_loc = getBestGroundingLocation( + unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); + + LightningTreeNodeSPtr new_parent; + LightningTreeNodeSPtr new_child; + this->attach(unsupported_location, grounding_loc, new_child, new_parent); + tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child)); + if (new_parent) + tree_node_locator.insert(std::make_pair(Point(new_parent->getLocation().x() / locator_cell_size, new_parent->getLocation().y() / locator_cell_size), new_parent)); + // update distance field + distance_field.update(grounding_loc.p(), unsupported_location); + } +} + +static bool polygonCollidesWithLineSegment(const Point from, const Point to, const EdgeGrid::Grid &loc_to_line) +{ + struct Visitor { + explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} + + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + if (Geometry::segments_intersect(segment.first, segment.second, line.a, line.b)) { + this->intersect = true; + return false; + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid& grid; + Line line; + bool intersect = false; + } visitor(loc_to_line); + + loc_to_line.visit_cells_intersecting_line(from, to, visitor); + return visitor.intersect; +} + +GroundingLocation LightningLayer::getBestGroundingLocation +( + const Point& unsupported_location, + const Polygons& current_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius, + const SparseLightningTreeNodeGrid& tree_node_locator, + const LightningTreeNodeSPtr& exclude_tree +) +{ + // Closest point on current_outlines to unsupported_location: + Point node_location; + { + double d2 = std::numeric_limits::max(); + for (const Polygon &contour : current_outlines) + if (contour.size() > 2) { + Point prev = contour.points.back(); + for (const Point &p2 : contour.points) { + if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) { + d2 = d; + node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast(); + } + prev = p2; + } + } + } + + const auto within_dist = coord_t((node_location - unsupported_location).cast().norm()); + + LightningTreeNodeSPtr sub_tree{ nullptr }; + coord_t current_dist = getWeightedDistance(node_location, unsupported_location); + if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. + const coord_t search_radius = std::min(current_dist, within_dist); + BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size)); + region.min /= locator_cell_size; + region.max /= locator_cell_size; + Point grid_addr; + for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y()) + for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) { + auto it_range = tree_node_locator.equal_range(grid_addr); + for (auto it = it_range.first; it != it_range.second; ++ it) { + auto candidate_sub_tree = it->second.lock(); + if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && + !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && + !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { + const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); + if (candidate_dist < current_dist) { + current_dist = candidate_dist; + sub_tree = candidate_sub_tree; + } + } + } + } + } + + return ! sub_tree ? + GroundingLocation{ nullptr, node_location } : + GroundingLocation{ sub_tree, std::optional() }; +} + +bool LightningLayer::attach( + const Point& unsupported_location, + const GroundingLocation& grounding_loc, + LightningTreeNodeSPtr& new_child, + LightningTreeNodeSPtr& new_root) +{ + // Update trees & distance fields. + if (grounding_loc.boundary_location) { + new_root = LightningTreeNode::create(grounding_loc.p(), std::make_optional(grounding_loc.p())); + new_child = new_root->addChild(unsupported_location); + tree_roots.push_back(new_root); + return true; + } else { + new_child = grounding_loc.tree_node->addChild(unsupported_location); + return false; + } +} + +void LightningLayer::reconnectRoots +( + std::vector& to_be_reconnected_tree_roots, + const Polygons& current_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius +) +{ + constexpr coord_t tree_connecting_ignore_offset = 100; + + SparseLightningTreeNodeGrid tree_node_locator; + fillLocator(tree_node_locator); + + const coord_t within_max_dist = outline_locator.resolution() * 2; + for (auto root_ptr : to_be_reconnected_tree_roots) + { + auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr); + + if (root_ptr->getLastGroundingLocation()) + { + const Point& ground_loc = root_ptr->getLastGroundingLocation().value(); + if (ground_loc != root_ptr->getLocation()) + { + Point new_root_pt; + // Find an intersection of the line segment from root_ptr->getLocation() to ground_loc, at within_max_dist from ground_loc. + if (lineSegmentPolygonsIntersection(root_ptr->getLocation(), ground_loc, current_outlines, outline_locator, new_root_pt, within_max_dist)) { + auto new_root = LightningTreeNode::create(new_root_pt, new_root_pt); + root_ptr->addChild(new_root); + new_root->reroot(); + + tree_node_locator.insert(std::make_pair(Point(new_root->getLocation().x() / locator_cell_size, new_root->getLocation().y() / locator_cell_size), new_root)); + + *old_root_it = std::move(new_root); // replace old root with new root + continue; + } + } + } + + const coord_t tree_connecting_ignore_width = wall_supporting_radius - tree_connecting_ignore_offset; // Ideally, the boundary size in which the valence rule is ignored would be configurable. + GroundingLocation ground = + getBestGroundingLocation + ( + root_ptr->getLocation(), + current_outlines, + outline_locator, + supporting_radius, + tree_connecting_ignore_width, + tree_node_locator, + root_ptr + ); + if (ground.boundary_location) + { + if (ground.boundary_location.value() == root_ptr->getLocation()) + continue; // Already on the boundary. + + auto new_root = LightningTreeNode::create(ground.p(), ground.p()); + auto attach_ptr = root_ptr->closestNode(new_root->getLocation()); + attach_ptr->reroot(); + + new_root->addChild(attach_ptr); + tree_node_locator.insert(std::make_pair(new_root->getLocation(), new_root)); + + *old_root_it = std::move(new_root); // replace old root with new root + } + else + { + assert(ground.tree_node); + assert(ground.tree_node != root_ptr); + assert(!root_ptr->hasOffspring(ground.tree_node)); + assert(!ground.tree_node->hasOffspring(root_ptr)); + + auto attach_ptr = root_ptr->closestNode(ground.tree_node->getLocation()); + attach_ptr->reroot(); + + ground.tree_node->addChild(attach_ptr); + + // remove old root + *old_root_it = std::move(tree_roots.back()); + tree_roots.pop_back(); + } + } +} + +/* + * Implementation assumes moving inside, but moving outside should just as well be possible. + */ +static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2) +{ + Point ret = from; + int64_t bestDist2 = std::numeric_limits::max(); + unsigned int bestPoly = static_cast(-1); + bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary + for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) + { + const Polygon &poly = polygons[poly_idx]; + if (poly.size() < 2) + continue; + Point p0 = poly[poly.size() - 2]; + Point p1 = poly.back(); + // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop + // to avoid integer rounding edge cases + bool projected_p_beyond_prev_segment = (p1 - p0).cast().dot((from - p0).cast()) >= (p1 - p0).cast().squaredNorm(); + for (const Point& p2 : poly) + { + // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A)); + // = A + (B-A) * ((B-A) dot (P-A)) / VSize2(B-A); + // X = P projected on AB + const Point& a = p1; + const Point& b = p2; + const Point& p = from; + Point ab = b - a; + Point ap = p - a; + int64_t ab_length2 = ab.cast().squaredNorm(); + if (ab_length2 <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other. + { + p1 = p2; //Skip only one of the points. + continue; + } + int64_t dot_prod = ab.cast().dot(ap.cast()); + if (dot_prod <= 0) // x is projected to before ab + { + if (projected_p_beyond_prev_segment) + { // case which looks like: > . + projected_p_beyond_prev_segment = false; + Point& x = p1; + + int64_t dist2 = (x - p).cast().squaredNorm(); + if (dist2 < bestDist2) + { + bestDist2 = dist2; + bestPoly = poly_idx; + if (distance == 0) { + ret = x; + } else { + // inward direction irrespective of sign of [distance] + Point inward_dir = perp((ab.cast().normalized() * scaled(10.0) + (p1 - p0).cast().normalized() * scaled(10.0)).eval()).cast(); + // MM2INT(10.0) to retain precision for the eventual normalization + ret = x + (inward_dir.cast().normalized() * distance).cast(); + is_already_on_correct_side_of_boundary = inward_dir.cast().dot((p - x).cast()) * distance >= 0; + } + } + } + else + { + projected_p_beyond_prev_segment = false; + p0 = p1; + p1 = p2; + continue; + } + } + else if (dot_prod >= ab_length2) // x is projected to beyond ab + { + projected_p_beyond_prev_segment = true; + p0 = p1; + p1 = p2; + continue; + } + else + { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . + projected_p_beyond_prev_segment = false; + Point x = a + ab * dot_prod / ab_length2; + + int64_t dist2 = (p - x).cast().squaredNorm(); + if (dist2 < bestDist2) + { + bestDist2 = dist2; + bestPoly = poly_idx; + if (distance == 0) { ret = x; } + else + { + // inward or outward depending on the sign of [distance] + Vec2d inward_dir = perp((ab.cast().normalized() * distance).eval()); + ret = x + inward_dir.cast(); + is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast()) >= 0; + } + } + } + p0 = p1; + p1 = p2; + } + } + if (is_already_on_correct_side_of_boundary) // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside + { + if (bestDist2 < distance * distance) + { + from = ret; + } + else + { + // from = from; // original point stays unaltered. It is already inside by enough distance + } + return bestPoly; + } + else if (bestDist2 < maxDist2) + { + from = ret; + return bestPoly; + } + return static_cast(-1); +} + +// Returns 'added someting'. +Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const +{ + Polygons result_lines; + if (tree_roots.empty()) + return result_lines; + + for (const auto& tree : tree_roots) + { + // If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon. + // (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.) + constexpr coord_t epsilon = 5; + Point should_be_inside = tree->getLocation(); + moveInside(limit_to_outline, should_be_inside, epsilon, epsilon * epsilon); + if (inside(limit_to_outline, should_be_inside)) + tree->convertToPolylines(result_lines, line_width); + } + + // TODO: allow for polylines! + Polygons split_lines; + for (Polygon &line : result_lines) + { + if (line.size() <= 1) continue; + Point last = line[0]; + for (size_t point_idx = 1; point_idx < line.size(); point_idx++) + { + Point here = line[point_idx]; + split_lines.push_back({ last, here }); + last = here; + } + } + + return split_lines; +} diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp new file mode 100644 index 0000000000..6f68d323f6 --- /dev/null +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -0,0 +1,87 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef LIGHTNING_LAYER_H +#define LIGHTNING_LAYER_H + +#include "../../EdgeGrid.hpp" +#include "../../Polygon.hpp" + +#include +#include +#include +#include + +namespace Slic3r +{ +class LightningTreeNode; + +using LightningTreeNodeSPtr = std::shared_ptr; +using SparseLightningTreeNodeGrid = std::unordered_multimap, PointHash>; + +struct GroundingLocation +{ + LightningTreeNodeSPtr tree_node; //!< not null if the gounding location is on a tree + std::optional boundary_location; //!< in case the gounding location is on the boundary + Point p() const; +}; + +/*! + * A layer of the lightning fill. + * + * Contains the trees to be printed and propagated to the next layer below. + */ +class LightningLayer +{ +public: + std::vector tree_roots; + + void generateNewTrees + ( + const Polygons& current_overhang, + const Polygons& current_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius + ); + + /*! Determine & connect to connection point in tree/outline. + * \param min_dist_from_boundary_for_tree If the unsupported point is closer to the boundary than this then don't consider connecting it to a tree + */ + GroundingLocation getBestGroundingLocation + ( + const Point& unsupported_location, + const Polygons& current_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius, + const SparseLightningTreeNodeGrid& tree_node_locator, + const LightningTreeNodeSPtr& exclude_tree = nullptr + ); + + /*! + * \param[out] new_child The new child node introduced + * \param[out] new_root The new root node if one had been made + * \return Whether a new root was added + */ + bool attach(const Point& unsupported_location, const GroundingLocation& ground, LightningTreeNodeSPtr& new_child, LightningTreeNodeSPtr& new_root); + + void reconnectRoots + ( + std::vector& to_be_reconnected_tree_roots, + const Polygons& current_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t supporting_radius, + const coord_t wall_supporting_radius + ); + + Polygons convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; + + coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); + + void fillLocator(SparseLightningTreeNodeGrid& tree_node_locator); +}; + +} // namespace Slic3r + +#endif // LIGHTNING_LAYER_H diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp new file mode 100644 index 0000000000..97bbbbc33c --- /dev/null +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -0,0 +1,408 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#include "TreeNode.hpp" + +#include "../../Geometry.hpp" + +using namespace Slic3r; + +coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const +{ + constexpr coord_t min_valence_for_boost = 0; + constexpr coord_t max_valence_for_boost = 4; + constexpr coord_t valence_boost_multiplier = 4; + + const size_t valence = (!m_is_root) + m_children.size(); + const coord_t valence_boost = (min_valence_for_boost < valence && valence < max_valence_for_boost) ? valence_boost_multiplier * supporting_radius : 0; + const auto dist_here = coord_t((getLocation() - unsupported_location).cast().norm()); + return dist_here - valence_boost; +} + +bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const +{ + if (to_be_checked == shared_from_this()) + return true; + + for (auto& child_ptr : m_children) + if (child_ptr->hasOffspring(to_be_checked)) + return true; + + return false; +} + +LightningTreeNodeSPtr LightningTreeNode::addChild(const Point& child_loc) +{ + assert(m_p != child_loc); + LightningTreeNodeSPtr child = LightningTreeNode::create(child_loc); + return addChild(child); +} + +LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_child) +{ + assert(new_child != shared_from_this()); + //assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio. + m_children.push_back(new_child); + new_child->m_parent = shared_from_this(); + new_child->m_is_root = false; + return new_child; +} + +void LightningTreeNode::propagateToNextLayer( + std::vector& next_trees, + const Polygons& next_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t prune_distance, + const coord_t smooth_magnitude, + const coord_t max_remove_colinear_dist) const +{ + auto tree_below = deepCopy(); + tree_below->prune(prune_distance); + tree_below->straighten(smooth_magnitude, max_remove_colinear_dist); + if (tree_below->realign(next_outlines, outline_locator, next_trees)) + next_trees.push_back(tree_below); +} + +// NOTE: Depth-first, as currently implemented. +// Skips the root (because that has no root itself), but all initial nodes will have the root point anyway. +void LightningTreeNode::visitBranches(const std::function& visitor) const +{ + for (const auto& node : m_children) { + assert(node->m_parent.lock() == shared_from_this()); + visitor(m_p, node->m_p); + node->visitBranches(visitor); + } +} + +// NOTE: Depth-first, as currently implemented. +void LightningTreeNode::visitNodes(const std::function& visitor) +{ + visitor(shared_from_this()); + for (const auto& node : m_children) { + assert(node->m_parent.lock() == shared_from_this()); + node->visitNodes(visitor); + } +} + +LightningTreeNode::LightningTreeNode(const Point& p, const std::optional& last_grounding_location /*= std::nullopt*/) : + m_is_root(true), m_p(p), m_last_grounding_location(last_grounding_location) +{} + +LightningTreeNodeSPtr LightningTreeNode::deepCopy() const +{ + LightningTreeNodeSPtr local_root = LightningTreeNode::create(m_p); + local_root->m_is_root = m_is_root; + if (m_is_root) + { + local_root->m_last_grounding_location = m_last_grounding_location.value_or(m_p); + } + local_root->m_children.reserve(m_children.size()); + for (const auto& node : m_children) + { + LightningTreeNodeSPtr child = node->deepCopy(); + child->m_parent = local_root; + local_root->m_children.push_back(child); + } + return local_root; +} + +void LightningTreeNode::reroot(LightningTreeNodeSPtr new_parent /*= nullptr*/) +{ + if (! m_is_root) { + auto old_parent = m_parent.lock(); + old_parent->reroot(shared_from_this()); + m_children.push_back(old_parent); + } + + if (new_parent) { + m_children.erase(std::remove(m_children.begin(), m_children.end(), new_parent), m_children.end()); + m_is_root = false; + m_parent = new_parent; + } else { + m_is_root = true; + m_parent.reset(); + } +} + +LightningTreeNodeSPtr LightningTreeNode::closestNode(const Point& loc) +{ + LightningTreeNodeSPtr result = shared_from_this(); + auto closest_dist2 = coord_t((m_p - loc).cast().norm()); + + for (const auto& child : m_children) { + LightningTreeNodeSPtr candidate_node = child->closestNode(loc); + const auto child_dist2 = coord_t((candidate_node->m_p - loc).cast().norm()); + if (child_dist2 < closest_dist2) { + closest_dist2 = child_dist2; + result = candidate_node; + } + } + + return result; +} + +bool inside(const Polygons &polygons, const Point p) +{ + int poly_count_inside = 0; + for (const Polygon &poly : polygons) { + const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points); + if (is_inside_this_poly == -1) + return true; + poly_count_inside += is_inside_this_poly; + } + return (poly_count_inside % 2) == 1; +} + +bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist) +{ + struct Visitor { + bool operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + if (Vec2d ip; Geometry::segment_segment_intersection(segment.first.cast(), segment.second.cast(), this->line_a, this->line_b, ip)) + if (double d = (this->intersection_pt - this->line_b).squaredNorm(); d < d2min) { + this->d2min = d; + this->intersection_pt = ip; + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid& grid; + Vec2d line_a; + Vec2d line_b; + Vec2d intersection_pt; + double d2min { std::numeric_limits::max() }; + } visitor { outline_locator, a.cast(), b.cast() }; + + outline_locator.visit_cells_intersecting_line(a, b, visitor); + return visitor.d2min < within_max_dist * within_max_dist; +} + +bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) +{ + if (outlines.empty()) + return false; + + if (inside(outlines, m_p)) { + // Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion: + Point coll; + bool reground_me = false; + m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const LightningTreeNodeSPtr &child) { + bool connect_branch = child->realign(outlines, outline_locator, rerooted_parts); + // Find an intersection of the line segment from p to child->p, at maximum outline_locator.resolution() * 2 distance from p. + if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outlines, outline_locator, coll, outline_locator.resolution() * 2)) { + child->m_last_grounding_location.reset(); + child->m_parent.reset(); + child->m_is_root = true; + rerooted_parts.push_back(child); + reground_me = true; + connect_branch = false; + } + return ! connect_branch; + }), m_children.end()); + if (reground_me) + m_last_grounding_location.reset(); + return true; + } + + // 'Lift' any decendants out of this tree: + for (auto& child : m_children) + if (child->realign(outlines, outline_locator, rerooted_parts)) { + child->m_last_grounding_location = m_p; + child->m_parent.reset(); + child->m_is_root = true; + rerooted_parts.push_back(child); + } + + m_children.clear(); + return false; +} + +void LightningTreeNode::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) +{ + straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist); +} + +LightningTreeNode::RectilinearJunction LightningTreeNode::straighten( + const coord_t magnitude, + const Point& junction_above, + const coord_t accumulated_dist, + const coord_t max_remove_colinear_dist2) +{ + constexpr coord_t junction_magnitude_factor_numerator = 3; + constexpr coord_t junction_magnitude_factor_denominator = 4; + + const coord_t junction_magnitude = magnitude * junction_magnitude_factor_numerator / junction_magnitude_factor_denominator; + if (m_children.size() == 1) + { + auto child_p = m_children.front(); + auto child_dist = coord_t((m_p - child_p->m_p).cast().norm()); + RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2); + coord_t total_dist_to_junction_below = junction_below.total_recti_dist; + Point a = junction_above; + Point b = junction_below.junction_loc; + if (a != b) // should always be true! + { + Point ab = b - a; + Point destination = a + ab * accumulated_dist / std::max(coord_t(1), total_dist_to_junction_below); + if ((destination - m_p).cast().squaredNorm() <= magnitude * magnitude) + m_p = destination; + else + m_p += ((destination - m_p).cast().normalized() * magnitude).cast(); + } + { // remove nodes on linear segments + constexpr coord_t close_enough = 10; + + child_p = m_children.front(); //recursive call to straighten might have removed the child + const LightningTreeNodeSPtr& parent_node = m_parent.lock(); + if (parent_node && + (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && + Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) { + child_p->m_parent = m_parent; + for (auto& sibling : parent_node->m_children) + { // find this node among siblings + if (sibling == shared_from_this()) + { + sibling = child_p; // replace this node by child + break; + } + } + } + } + return junction_below; + } + else + { + constexpr coord_t weight = 1000; + Point junction_moving_dir = ((junction_above - m_p).cast().normalized() * weight).cast(); + bool prevent_junction_moving = false; + for (auto& child_p : m_children) + { + const auto child_dist = coord_t((m_p - child_p->m_p).cast().norm()); + RectilinearJunction below = child_p->straighten(magnitude, m_p, child_dist, max_remove_colinear_dist2); + + junction_moving_dir += ((below.junction_loc - m_p).cast().normalized() * weight).cast(); + if (below.total_recti_dist < magnitude) // TODO: make configurable? + { + prevent_junction_moving = true; // prevent flipflopping in branches due to straightening and junctoin moving clashing + } + } + if (junction_moving_dir != Point(0, 0) && ! m_children.empty() && ! m_is_root && ! prevent_junction_moving) + { + auto junction_moving_dir_len = coord_t(junction_moving_dir.norm()); + if (junction_moving_dir_len > junction_magnitude) + { + junction_moving_dir = junction_moving_dir * junction_magnitude / junction_moving_dir_len; + } + m_p += junction_moving_dir; + } + return RectilinearJunction{ accumulated_dist, m_p }; + } +} + +// Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. +coord_t LightningTreeNode::prune(const coord_t& pruning_distance) +{ + if (pruning_distance <= 0) + return 0; + + coord_t max_distance_pruned = 0; + for (auto child_it = m_children.begin(); child_it != m_children.end(); ) { + auto& child = *child_it; + coord_t dist_pruned_child = child->prune(pruning_distance); + if (dist_pruned_child >= pruning_distance) + { // pruning is finished for child; dont modify further + max_distance_pruned = std::max(max_distance_pruned, dist_pruned_child); + ++child_it; + } else { + const Point a = getLocation(); + const Point b = child->getLocation(); + const Point ba = a - b; + const auto ab_len = coord_t(ba.cast().norm()); + if (dist_pruned_child + ab_len <= pruning_distance) { + // we're still in the process of pruning + assert(child->m_children.empty() && "when pruning away a node all it's children must already have been pruned away"); + max_distance_pruned = std::max(max_distance_pruned, dist_pruned_child + ab_len); + child_it = m_children.erase(child_it); + } else { + // pruning stops in between this node and the child + const Point n = b + (ba.cast().normalized() * (pruning_distance - dist_pruned_child)).cast(); + assert(std::abs((n - b).cast().norm() + dist_pruned_child - pruning_distance) < 10 && "total pruned distance must be equal to the pruning_distance"); + max_distance_pruned = std::max(max_distance_pruned, pruning_distance); + child->setLocation(n); + ++child_it; + } + } + } + + return max_distance_pruned; +} + +void LightningTreeNode::convertToPolylines(Polygons& output, const coord_t line_width) const +{ + Polygons result; + output.emplace_back(); + convertToPolylines(0, result); + removeJunctionOverlap(result, line_width); + append(output, std::move(result)); +} + +void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& output) const +{ + if (m_children.empty()) { + output[long_line_idx].points.push_back(m_p); + return; + } + size_t first_child_idx = rand() % m_children.size(); + m_children[first_child_idx]->convertToPolylines(long_line_idx, output); + output[long_line_idx].points.push_back(m_p); + + for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) { + size_t child_idx = (first_child_idx + idx_offset) % m_children.size(); + const LightningTreeNode& child = *m_children[child_idx]; + output.emplace_back(); + size_t child_line_idx = output.size() - 1; + child.convertToPolylines(child_line_idx, output); + output[child_line_idx].points.emplace_back(m_p); + } +} + +void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const +{ + const coord_t reduction = line_width / 2; // TODO make configurable? + for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) { + Polygon &polyline = *poly_it; + if (polyline.size() <= 1) { + polyline = std::move(result_lines.back()); + result_lines.pop_back(); + continue; + } + + coord_t to_be_reduced = reduction; + Point a = polyline.back(); + for (int point_idx = polyline.size() - 2; point_idx >= 0; point_idx--) { + const Point b = polyline[point_idx]; + const Point ab = b - a; + const auto ab_len = coord_t(ab.cast().norm()); + if (ab_len >= to_be_reduced) { + polyline.points.back() = a + (ab.cast() * (double(to_be_reduced) / ab_len)).cast(); + break; + } else { + to_be_reduced -= ab_len; + polyline.points.pop_back(); + } + a = b; + } + + if (polyline.size() <= 1) { + polyline = std::move(result_lines.back()); + result_lines.pop_back(); + } else + ++ poly_it; + } +} diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp new file mode 100644 index 0000000000..1ca4b01d6a --- /dev/null +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -0,0 +1,275 @@ +//Copyright (c) 2021 Ultimaker B.V. +//CuraEngine is released under the terms of the AGPLv3 or higher. + +#ifndef LIGHTNING_TREE_NODE_H +#define LIGHTNING_TREE_NODE_H + +#include +#include +#include +#include + +#include "../../EdgeGrid.hpp" +#include "../../Polygon.hpp" + +namespace Slic3r +{ + +constexpr auto locator_cell_size = scaled(4.); + +class LightningTreeNode; + +using LightningTreeNodeSPtr = std::shared_ptr; + +// NOTE: As written, this struct will only be valid for a single layer, will have to be updated for the next. +// NOTE: Reasons for implementing this with some separate closures: +// - keep clear deliniation during development +// - possibility of multiple distance field strategies + +/*! + * A single vertex of a Lightning Tree, the structure that determines the paths + * to be printed to form Lightning Infill. + * + * In essence these vertices are just a position linked to other positions in + * 2D. The nodes have a hierarchical structure of parents and children, forming + * a tree. The class also has some helper functions specific to Lightning Infill + * e.g. to straighten the paths around this node. + */ +class LightningTreeNode : public std::enable_shared_from_this +{ +public: + // Workaround for private/protected constructors and 'make_shared': https://stackoverflow.com/a/27832765 + template LightningTreeNodeSPtr static create(Arg&&...arg) + { + struct EnableMakeShared : public LightningTreeNode + { + EnableMakeShared(Arg&&...arg) : LightningTreeNode(std::forward(arg)...) {} + }; + return std::make_shared(std::forward(arg)...); + } + + /*! + * Get the position on this layer that this node represents, a vertex of the + * path to print. + * \return The position that this node represents. + */ + const Point& getLocation() const { return m_p; } + + /*! + * Change the position on this layer that the node represents. + * \param p The position that the node needs to represent. + */ + void setLocation(const Point& p) { m_p = p; } + + /*! + * Construct a new ``LightningTreeNode`` instance and add it as a child of + * this node. + * \param p The location of the new node. + * \return A shared pointer to the new node. + */ + LightningTreeNodeSPtr addChild(const Point& p); + + /*! + * Add an existing ``LightningTreeNode`` as a child of this node. + * \param new_child The node that must be added as a child. + * \return Always returns \p new_child. + */ + LightningTreeNodeSPtr addChild(LightningTreeNodeSPtr& new_child); + + /*! + * Propagate this node's sub-tree to the next layer. + * + * Creates a copy of this tree, realign it to the new layer boundaries + * \p next_outlines and reduce (i.e. prune and straighten) it. A copy of + * this node and all of its descendant nodes will be added to the + * \p next_trees vector. + * \param next_trees A collection of tree nodes to use for the next layer. + * \param next_outlines The shape of the layer below, to make sure that the + * tree stays within the bounds of the infill area. + * \param prune_distance The maximum distance that a leaf node may be moved + * such that it still supports the current node. + * \param smooth_magnitude The maximum distance that a line may be shifted + * to straighten the tree's paths, such that it still supports the current + * paths. + * \param max_remove_colinear_dist The maximum distance of a line-segment + * from which straightening may remove a colinear point. + */ + void propagateToNextLayer + ( + std::vector& next_trees, + const Polygons& next_outlines, + const EdgeGrid::Grid& outline_locator, + const coord_t prune_distance, + const coord_t smooth_magnitude, + const coord_t max_remove_colinear_dist + ) const; + + /*! + * Executes a given function for every line segment in this node's sub-tree. + * + * The function takes two `Point` arguments. These arguments will be filled + * in with the higher-order node (closer to the root) first, and the + * downtree node (closer to the leaves) as the second argument. The segment + * from this node's parent to this node itself is not included. + * The order in which the segments are visited is depth-first. + * \param visitor A function to execute for every branch in the node's sub- + * tree. + */ + void visitBranches(const std::function& visitor) const; + + /*! + * Execute a given function for every node in this node's sub-tree. + * + * The visitor function takes a node as input. This node is not const, so + * this can be used to change the tree. + * Nodes are visited in depth-first order. This node itself is visited as + * well (pre-order). + * \param visitor A function to execute for every node in this node's sub- + * tree. + */ + void visitNodes(const std::function& visitor); + + /*! + * Get a weighted distance from an unsupported point to this node (given the current supporting radius). + * + * When attaching a unsupported location to a node, not all nodes have the same priority. + * (Eucludian) closer nodes are prioritised, but that's not the whole story. + * For instance, we give some nodes a 'valence boost' depending on the nr. of branches. + * \param unsupported_location The (unsuppported) location of which the weighted distance needs to be calculated. + * \param supporting_radius The maximum distance which can be bridged without (infill) supporting it. + * \return The weighted distance. + */ + coord_t getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const; + + /*! + * Returns whether this node is the root of a lightning tree. It is the root + * if it has no parents. + * \return ``true`` if this node is the root (no parents) or ``false`` if it + * is a child node of some other node. + */ + bool isRoot() const { return m_is_root; } + + /*! + * Reverse the parent-child relationship all the way to the root, from this node onward. + * This has the effect of 're-rooting' the tree at the current node if no immediate parent is given as argument. + * That is, the current node will become the root, it's (former) parent if any, will become one of it's children. + * This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf. + * \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree. + */ + void reroot(LightningTreeNodeSPtr new_parent = nullptr); + + /*! + * Retrieves the closest node to the specified location. + * \param loc The specified location. + * \result The branch that starts at the position closest to the location within this tree. + */ + LightningTreeNodeSPtr closestNode(const Point& loc); + + /*! + * Returns whether the given tree node is a descendant of this node. + * + * If this node itself is given, it is also considered to be a descendant. + * \param to_be_checked A node to find out whether it is a descendant of + * this node. + * \return ``true`` if the given node is a descendant or this node itself, + * or ``false`` if it is not in the sub-tree. + */ + bool hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const; + +protected: + LightningTreeNode() = delete; // Don't allow empty contruction + + /*! + * Construct a new node, either for insertion in a tree or as root. + * \param p The physical location in the 2D layer that this node represents. + * Connecting other nodes to this node indicates that a line segment should + * be drawn between those two physical positions. + */ + LightningTreeNode(const Point& p, const std::optional& last_grounding_location = std::nullopt); + + /*! + * Copy this node and its entire sub-tree. + * \return The equivalent of this node in the copy (the root of the new sub- + * tree). + */ + LightningTreeNodeSPtr deepCopy() const; + + /*! Reconnect trees from the layer above to the new outlines of the lower layer. + * \return Wether or not the root is kept (false is no, true is yes). + */ + bool realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts); + + struct RectilinearJunction + { + coord_t total_recti_dist; //!< rectilinear distance along the tree from the last junction above to the junction below + Point junction_loc; //!< junction location below + }; + + /*! + * Smoothen the tree to make it a bit more printable, while still supporting + * the trees above. + * \param magnitude The maximum allowed distance to move the node. + * \param max_remove_colinear_dist Maximum distance of the (compound) line-segment from which a co-linear point may be removed. + */ + void straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist); + + /*! Recursive part of \ref straighten(.) + * \param junction_above The last seen junction with multiple children above + * \param accumulated_dist The distance along the tree from the last seen junction to this node + * \param max_remove_colinear_dist2 Maximum distance _squared_ of the (compound) line-segment from which a co-linear point may be removed. + * \return the total distance along the tree from the last junction above to the first next junction below and the location of the next junction below + */ + RectilinearJunction straighten(const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, const coord_t max_remove_colinear_dist2); + + /*! Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. + * \return The distance that has been pruned. If less than \p distance, then the whole tree was puned away. + */ + coord_t prune(const coord_t& distance); + +public: + /*! + * Convert the tree into polylines + * + * At each junction one line is chosen at random to continue + * + * The lines start at a leaf and end in a junction + * + * \param output all branches in this tree connected into polylines + */ + void convertToPolylines(Polygons& output, const coord_t line_width) const; + + /*! If this was ever a direct child of the root, it'll have a previous grounding location. + * + * This needs to be known when roots are reconnected, so that the last (higher) layer is supported by the next one. + */ + const std::optional& getLastGroundingLocation() const { return m_last_grounding_location; } + +protected: + /*! + * Convert the tree into polylines + * + * At each junction one line is chosen at random to continue + * + * The lines start at a leaf and end in a junction + * + * \param long_line a reference to a polyline in \p output which to continue building on in the recursion + * \param output all branches in this tree connected into polylines + */ + void convertToPolylines(size_t long_line_idx, Polygons& output) const; + + void removeJunctionOverlap(Polygons& polylines, const coord_t line_width) const; + + bool m_is_root; + Point m_p; + std::weak_ptr m_parent; + std::vector m_children; + + std::optional m_last_grounding_location; //()(pt.x()) ^ std::hash()(pt.y()); + return coord_t((89 * 31 + int64_t(pt.x())) * 31 + pt.y()); } }; From 481def3205182193140c6186420fe97adbda8abc Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 1 Dec 2021 18:11:41 +0100 Subject: [PATCH 41/50] WIP Lightning Infill, roughly integrated, untested, disabled with HAS_LIGHTNING_INFILL --- src/libslic3r/CMakeLists.txt | 6 +- src/libslic3r/Fill/FillBase.cpp | 4 ++ src/libslic3r/Fill/FillLightning.cpp | 28 ++++++++ src/libslic3r/Fill/FillLightning.hpp | 36 ++++++++++ .../Fill/Lightning/DistanceField.cpp | 8 +-- .../Fill/Lightning/DistanceField.hpp | 8 +-- src/libslic3r/Fill/Lightning/Generator.cpp | 20 +++--- src/libslic3r/Fill/Lightning/Generator.hpp | 14 ++-- src/libslic3r/Fill/Lightning/Layer.cpp | 68 +++++++++---------- src/libslic3r/Fill/Lightning/Layer.hpp | 28 ++++---- src/libslic3r/Fill/Lightning/TreeNode.cpp | 60 ++++++++-------- src/libslic3r/Fill/Lightning/TreeNode.hpp | 48 ++++++------- src/libslic3r/PrintConfig.cpp | 11 ++- src/libslic3r/PrintConfig.hpp | 8 ++- 14 files changed, 221 insertions(+), 126 deletions(-) create mode 100644 src/libslic3r/Fill/FillLightning.cpp create mode 100644 src/libslic3r/Fill/FillLightning.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6d5f18fc6e..6f5b264200 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -67,8 +67,8 @@ add_library(libslic3r STATIC Fill/FillPlanePath.hpp Fill/FillLine.cpp Fill/FillLine.hpp - Fill/FillRectilinear.cpp - Fill/FillRectilinear.hpp + Fill/FillLightning.cpp + Fill/FillLightning.hpp Fill/Lightning/DistanceField.cpp Fill/Lightning/DistanceField.hpp Fill/Lightning/Generator.cpp @@ -77,6 +77,8 @@ add_library(libslic3r STATIC Fill/Lightning/Layer.hpp Fill/Lightning/TreeNode.cpp Fill/Lightning/TreeNode.hpp + Fill/FillRectilinear.cpp + Fill/FillRectilinear.hpp Flow.cpp Flow.hpp format.hpp diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 52653f9759..7ca0699c0f 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -19,6 +19,7 @@ #include "FillLine.hpp" #include "FillRectilinear.hpp" #include "FillAdaptive.hpp" +#include "FillLightning.hpp" // #define INFILL_DEBUG_OUTPUT @@ -45,6 +46,9 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler(); case ipSupportBase: return new FillSupportBase(); +#if HAS_LIGHTNING_INFILL + case ipLightning: return new FillLightning::Filler(); +#endif // HAS_LIGHTNING_INFILL default: throw Slic3r::InvalidArgument("unknown type"); } } diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp new file mode 100644 index 0000000000..52c3ce4c59 --- /dev/null +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -0,0 +1,28 @@ +#include "../Print.hpp" + +#include "FillLightning.hpp" +#include "Lightning/Generator.hpp" + +#include +#include +#include +#include + +namespace Slic3r::FillLightning { + +Polylines Filler::fill_surface(const Surface *surface, const FillParams ¶ms) +{ + const Layer &layer = generator->getTreesForLayer(this->layer_id); + return layer.convertToLines(to_polygons(surface->expolygon), generator->infilll_extrusion_width()); +} + +void GeneratorDeleter::operator()(Generator *p) { + delete p; +} + +GeneratorPtr build_generator(const PrintObject &print_object) +{ + return GeneratorPtr(new Generator(print_object)); +} + +} // namespace Slic3r::FillAdaptive diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp new file mode 100644 index 0000000000..b4e7e35f11 --- /dev/null +++ b/src/libslic3r/Fill/FillLightning.hpp @@ -0,0 +1,36 @@ +#ifndef slic3r_FillLightning_hpp_ +#define slic3r_FillLightning_hpp_ + +#include "FillBase.hpp" + +namespace Slic3r { + +class PrintObject; + +namespace FillLightning { + +class Generator; +// To keep the definition of Octree opaque, we have to define a custom deleter. +struct GeneratorDeleter { void operator()(Generator *p); }; +using GeneratorPtr = std::unique_ptr; + +GeneratorPtr build_generator(const PrintObject &print_object); + +class Filler : public Slic3r::Fill +{ +public: + ~Filler() override = default; + + Generator *generator { nullptr }; +protected: + Fill* clone() const override { return new Filler(*this); } + // Perform the fill. + Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + // Let the G-code export reoder the infill lines. + bool no_sort() const override { return false; } +}; + +} // namespace FillAdaptive +} // namespace Slic3r + +#endif // slic3r_FillLightning_hpp_ diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 0a731915dd..125da92228 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -4,12 +4,12 @@ #include "DistanceField.hpp" //Class we're implementing. #include "../FillRectilinear.hpp" -namespace Slic3r +namespace Slic3r::FillLightning { constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient. -LightningDistanceField::LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : +DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : m_cell_size(radius / radius_per_cell_size), m_supporting_radius(radius) { @@ -44,7 +44,7 @@ LightningDistanceField::LightningDistanceField(const coord_t& radius, const Poly } } -void LightningDistanceField::update(const Point& to_node, const Point& added_leaf) +void DistanceField::update(const Point& to_node, const Point& added_leaf) { Vec2d v = (added_leaf - to_node).cast(); auto l2 = v.squaredNorm(); @@ -95,4 +95,4 @@ void LightningDistanceField::update(const Point& to_node, const Point& added_lea } } -} // namespace Slic3r +} // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index 9d761a03ba..6e550a7e25 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -4,7 +4,7 @@ #ifndef LIGHTNING_DISTANCE_FIELD_H #define LIGHTNING_DISTANCE_FIELD_H -namespace Slic3r +namespace Slic3r::FillLightning { /*! @@ -15,7 +15,7 @@ namespace Slic3r * maintains how far it is removed from the edge, which is used to determine * how it gets supported by Lightning Infill. */ -class LightningDistanceField +class DistanceField { public: /*! @@ -26,7 +26,7 @@ public: * \param current_overhang The overhang that needs to be supported on this * layer. */ - LightningDistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); + DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); /*! * Gets the next unsupported location to be supported by a new branch. @@ -92,6 +92,6 @@ protected: std::unordered_map::iterator, PointHash> m_unsupported_points_grid; }; -} // namespace Slic3r +} // namespace Slic3r::FillLightning #endif //LIGHTNING_DISTANCE_FIELD_H diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index dfe0d9e35b..5d935b1042 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -20,12 +20,12 @@ * - Make a pass with Arachne over the output. Somehow. * - Generate all to-be-supported points at once instead of sequentially: See branch interlocking_gen PolygonUtils::spreadDots (Or work with sparse grids.) * - Lots of magic values ... to many to parameterize. But are they the best? - * - Move more complex computations from LightningGenerator constructor to elsewhere. + * - Move more complex computations from Generator constructor to elsewhere. */ -using namespace Slic3r; +namespace Slic3r::FillLightning { -LightningGenerator::LightningGenerator(const PrintObject &print_object) +Generator::Generator(const PrintObject &print_object) { const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &object_config = print_object.config(); @@ -51,7 +51,7 @@ LightningGenerator::LightningGenerator(const PrintObject &print_object) generateTrees(print_object); } -void LightningGenerator::generateInitialInternalOverhangs(const PrintObject &print_object) +void Generator::generateInitialInternalOverhangs(const PrintObject &print_object) { m_overhang_per_layer.resize(print_object.layers().size()); const float infill_wall_offset = - m_infill_extrusion_width; @@ -73,13 +73,13 @@ void LightningGenerator::generateInitialInternalOverhangs(const PrintObject &pri } } -const LightningLayer& LightningGenerator::getTreesForLayer(const size_t& layer_id) const +const Layer& Generator::getTreesForLayer(const size_t& layer_id) const { assert(layer_id < m_lightning_layers.size()); return m_lightning_layers[layer_id]; } -void LightningGenerator::generateTrees(const PrintObject &print_object) +void Generator::generateTrees(const PrintObject &print_object) { m_lightning_layers.resize(print_object.layers().size()); const coord_t infill_wall_offset = - m_infill_extrusion_width; @@ -101,11 +101,11 @@ void LightningGenerator::generateTrees(const PrintObject &print_object) // For-each layer from top to bottom: for (int layer_id = top_layer_id; layer_id >= 0; layer_id--) { - LightningLayer& current_lightning_layer = m_lightning_layers[layer_id]; + Layer& current_lightning_layer = m_lightning_layers[layer_id]; Polygons& current_outlines = infill_outlines[layer_id]; // register all trees propagated from the previous layer as to-be-reconnected - std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; + std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); @@ -118,8 +118,10 @@ void LightningGenerator::generateTrees(const PrintObject &print_object) outlines_locator.set_bbox(get_extents(below_outlines).inflated(SCALED_EPSILON)); outlines_locator.create(below_outlines, locator_cell_size); - std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; + std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; for (auto& tree : current_lightning_layer.tree_roots) tree->propagateToNextLayer(lower_trees, below_outlines, outlines_locator, m_prune_length, m_straightening_max_distance, locator_cell_size / 2); } } + +} // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp index 7de05d2031..c44ecfe7a2 100644 --- a/src/libslic3r/Fill/Lightning/Generator.hpp +++ b/src/libslic3r/Fill/Lightning/Generator.hpp @@ -14,6 +14,9 @@ namespace Slic3r { class PrintObject; +namespace FillLightning +{ + /*! * Generates the Lightning Infill pattern. * @@ -31,7 +34,7 @@ class PrintObject; * Printing of Hollowed Objects" by Tricard, Claux and Lefebvre: * https://www.researchgate.net/publication/333808588_Ribbed_Support_Vaults_for_3D_Printing_of_Hollowed_Objects */ -class LightningGenerator // "Just like Nicola used to make!" +class Generator // "Just like Nicola used to make!" { public: /*! @@ -42,7 +45,7 @@ public: * already be calculated at this point. * \param mesh The mesh to generate infill for. */ - LightningGenerator(const PrintObject &print_object); + Generator(const PrintObject &print_object); /*! * Get a tree of paths generated for a certain layer of the mesh. @@ -53,7 +56,9 @@ public: * \return A tree structure representing paths to print to create the * Lightning Infill pattern. */ - const LightningLayer& getTreesForLayer(const size_t& layer_id) const; + const Layer& getTreesForLayer(const size_t& layer_id) const; + + float infilll_extrusion_width() const { return m_infill_extrusion_width; } protected: /*! @@ -118,9 +123,10 @@ protected: * * This is generated by \ref generateTrees. */ - std::vector m_lightning_layers; + std::vector m_lightning_layers; }; +} // namespace FillLightning } // namespace Slic3r #endif // LIGHTNING_GENERATOR_H diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 62f1617f9a..19bf7ec481 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -10,9 +10,9 @@ #include "../../Geometry.hpp" -using namespace Slic3r; +namespace Slic3r::FillLightning { -coord_t LightningLayer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location) +coord_t Layer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location) { return coord_t((boundary_loc - unsupported_location).cast().norm()); } @@ -23,16 +23,16 @@ Point GroundingLocation::p() const return tree_node ? tree_node->getLocation() : *boundary_location; } -void LightningLayer::fillLocator(SparseLightningTreeNodeGrid &tree_node_locator) +void Layer::fillLocator(SparseNodeGrid &tree_node_locator) { - std::function add_node_to_locator_func = [&tree_node_locator](LightningTreeNodeSPtr node) { + std::function add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) { tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node)); }; for (auto& tree : tree_roots) tree->visitNodes(add_node_to_locator_func); } -void LightningLayer::generateNewTrees +void Layer::generateNewTrees ( const Polygons& current_overhang, const Polygons& current_outlines, @@ -41,9 +41,9 @@ void LightningLayer::generateNewTrees const coord_t wall_supporting_radius ) { - LightningDistanceField distance_field(supporting_radius, current_outlines, current_overhang); + DistanceField distance_field(supporting_radius, current_outlines, current_overhang); - SparseLightningTreeNodeGrid tree_node_locator; + SparseNodeGrid tree_node_locator; fillLocator(tree_node_locator); // Until no more points need to be added to support all: @@ -53,8 +53,8 @@ void LightningLayer::generateNewTrees GroundingLocation grounding_loc = getBestGroundingLocation( unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); - LightningTreeNodeSPtr new_parent; - LightningTreeNodeSPtr new_child; + NodeSPtr new_parent; + NodeSPtr new_child; this->attach(unsupported_location, grounding_loc, new_child, new_parent); tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child)); if (new_parent) @@ -93,15 +93,15 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con return visitor.intersect; } -GroundingLocation LightningLayer::getBestGroundingLocation +GroundingLocation Layer::getBestGroundingLocation ( const Point& unsupported_location, const Polygons& current_outlines, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius, - const SparseLightningTreeNodeGrid& tree_node_locator, - const LightningTreeNodeSPtr& exclude_tree + const SparseNodeGrid& tree_node_locator, + const NodeSPtr& exclude_tree ) { // Closest point on current_outlines to unsupported_location: @@ -123,7 +123,7 @@ GroundingLocation LightningLayer::getBestGroundingLocation const auto within_dist = coord_t((node_location - unsupported_location).cast().norm()); - LightningTreeNodeSPtr sub_tree{ nullptr }; + NodeSPtr sub_tree{ nullptr }; coord_t current_dist = getWeightedDistance(node_location, unsupported_location); if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. const coord_t search_radius = std::min(current_dist, within_dist); @@ -154,15 +154,15 @@ GroundingLocation LightningLayer::getBestGroundingLocation GroundingLocation{ sub_tree, std::optional() }; } -bool LightningLayer::attach( +bool Layer::attach( const Point& unsupported_location, const GroundingLocation& grounding_loc, - LightningTreeNodeSPtr& new_child, - LightningTreeNodeSPtr& new_root) + NodeSPtr& new_child, + NodeSPtr& new_root) { // Update trees & distance fields. if (grounding_loc.boundary_location) { - new_root = LightningTreeNode::create(grounding_loc.p(), std::make_optional(grounding_loc.p())); + new_root = Node::create(grounding_loc.p(), std::make_optional(grounding_loc.p())); new_child = new_root->addChild(unsupported_location); tree_roots.push_back(new_root); return true; @@ -172,9 +172,9 @@ bool LightningLayer::attach( } } -void LightningLayer::reconnectRoots +void Layer::reconnectRoots ( - std::vector& to_be_reconnected_tree_roots, + std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, @@ -183,7 +183,7 @@ void LightningLayer::reconnectRoots { constexpr coord_t tree_connecting_ignore_offset = 100; - SparseLightningTreeNodeGrid tree_node_locator; + SparseNodeGrid tree_node_locator; fillLocator(tree_node_locator); const coord_t within_max_dist = outline_locator.resolution() * 2; @@ -198,8 +198,8 @@ void LightningLayer::reconnectRoots { Point new_root_pt; // Find an intersection of the line segment from root_ptr->getLocation() to ground_loc, at within_max_dist from ground_loc. - if (lineSegmentPolygonsIntersection(root_ptr->getLocation(), ground_loc, current_outlines, outline_locator, new_root_pt, within_max_dist)) { - auto new_root = LightningTreeNode::create(new_root_pt, new_root_pt); + if (lineSegmentPolygonsIntersection(root_ptr->getLocation(), ground_loc, outline_locator, new_root_pt, within_max_dist)) { + auto new_root = Node::create(new_root_pt, new_root_pt); root_ptr->addChild(new_root); new_root->reroot(); @@ -228,7 +228,7 @@ void LightningLayer::reconnectRoots if (ground.boundary_location.value() == root_ptr->getLocation()) continue; // Already on the boundary. - auto new_root = LightningTreeNode::create(ground.p(), ground.p()); + auto new_root = Node::create(ground.p(), ground.p()); auto attach_ptr = root_ptr->closestNode(new_root->getLocation()); attach_ptr->reroot(); @@ -375,14 +375,13 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan } // Returns 'added someting'. -Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const +Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const { - Polygons result_lines; if (tree_roots.empty()) - return result_lines; + return {}; - for (const auto& tree : tree_roots) - { + Polygons result_lines; + for (const auto& tree : tree_roots) { // If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon. // (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.) constexpr coord_t epsilon = 5; @@ -393,13 +392,12 @@ Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const } // TODO: allow for polylines! - Polygons split_lines; - for (Polygon &line : result_lines) - { - if (line.size() <= 1) continue; + Polylines split_lines; + for (Polygon &line : result_lines) { + if (line.size() <= 1) + continue; Point last = line[0]; - for (size_t point_idx = 1; point_idx < line.size(); point_idx++) - { + for (size_t point_idx = 1; point_idx < line.size(); point_idx++) { Point here = line[point_idx]; split_lines.push_back({ last, here }); last = here; @@ -408,3 +406,5 @@ Polygons LightningLayer::convertToLines(const Polygons& limit_to_outline, const return split_lines; } + +} // namespace Slic3r::Lightning diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index 6f68d323f6..7ef5eaec19 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -12,16 +12,16 @@ #include #include -namespace Slic3r +namespace Slic3r::FillLightning { -class LightningTreeNode; -using LightningTreeNodeSPtr = std::shared_ptr; -using SparseLightningTreeNodeGrid = std::unordered_multimap, PointHash>; +class Node; +using NodeSPtr = std::shared_ptr; +using SparseNodeGrid = std::unordered_multimap, PointHash>; struct GroundingLocation { - LightningTreeNodeSPtr tree_node; //!< not null if the gounding location is on a tree + NodeSPtr tree_node; //!< not null if the gounding location is on a tree std::optional boundary_location; //!< in case the gounding location is on the boundary Point p() const; }; @@ -31,10 +31,10 @@ struct GroundingLocation * * Contains the trees to be printed and propagated to the next layer below. */ -class LightningLayer +class Layer { public: - std::vector tree_roots; + std::vector tree_roots; void generateNewTrees ( @@ -55,8 +55,8 @@ public: const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius, - const SparseLightningTreeNodeGrid& tree_node_locator, - const LightningTreeNodeSPtr& exclude_tree = nullptr + const SparseNodeGrid& tree_node_locator, + const NodeSPtr& exclude_tree = nullptr ); /*! @@ -64,24 +64,24 @@ public: * \param[out] new_root The new root node if one had been made * \return Whether a new root was added */ - bool attach(const Point& unsupported_location, const GroundingLocation& ground, LightningTreeNodeSPtr& new_child, LightningTreeNodeSPtr& new_root); + bool attach(const Point& unsupported_location, const GroundingLocation& ground, NodeSPtr& new_child, NodeSPtr& new_root); void reconnectRoots ( - std::vector& to_be_reconnected_tree_roots, + std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius ); - Polygons convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; + Polylines convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); - void fillLocator(SparseLightningTreeNodeGrid& tree_node_locator); + void fillLocator(SparseNodeGrid& tree_node_locator); }; -} // namespace Slic3r +} // namespace Slic3r::FillLightning #endif // LIGHTNING_LAYER_H diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 97bbbbc33c..2cb2b5f6d4 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -5,9 +5,9 @@ #include "../../Geometry.hpp" -using namespace Slic3r; +namespace Slic3r::FillLightning { -coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const +coord_t Node::getWeightedDistance(const Point& unsupported_location, const coord_t& supporting_radius) const { constexpr coord_t min_valence_for_boost = 0; constexpr coord_t max_valence_for_boost = 4; @@ -19,7 +19,7 @@ coord_t LightningTreeNode::getWeightedDistance(const Point& unsupported_location return dist_here - valence_boost; } -bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const +bool Node::hasOffspring(const NodeSPtr& to_be_checked) const { if (to_be_checked == shared_from_this()) return true; @@ -31,14 +31,14 @@ bool LightningTreeNode::hasOffspring(const LightningTreeNodeSPtr& to_be_checked) return false; } -LightningTreeNodeSPtr LightningTreeNode::addChild(const Point& child_loc) +NodeSPtr Node::addChild(const Point& child_loc) { assert(m_p != child_loc); - LightningTreeNodeSPtr child = LightningTreeNode::create(child_loc); + NodeSPtr child = Node::create(child_loc); return addChild(child); } -LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_child) +NodeSPtr Node::addChild(NodeSPtr& new_child) { assert(new_child != shared_from_this()); //assert(p != new_child->p); // NOTE: No problem for now. Issue to solve later. Maybe even afetr final. Low prio. @@ -48,8 +48,8 @@ LightningTreeNodeSPtr LightningTreeNode::addChild(LightningTreeNodeSPtr& new_chi return new_child; } -void LightningTreeNode::propagateToNextLayer( - std::vector& next_trees, +void Node::propagateToNextLayer( + std::vector& next_trees, const Polygons& next_outlines, const EdgeGrid::Grid& outline_locator, const coord_t prune_distance, @@ -65,7 +65,7 @@ void LightningTreeNode::propagateToNextLayer( // NOTE: Depth-first, as currently implemented. // Skips the root (because that has no root itself), but all initial nodes will have the root point anyway. -void LightningTreeNode::visitBranches(const std::function& visitor) const +void Node::visitBranches(const std::function& visitor) const { for (const auto& node : m_children) { assert(node->m_parent.lock() == shared_from_this()); @@ -75,7 +75,7 @@ void LightningTreeNode::visitBranches(const std::function& visitor) +void Node::visitNodes(const std::function& visitor) { visitor(shared_from_this()); for (const auto& node : m_children) { @@ -84,13 +84,13 @@ void LightningTreeNode::visitNodes(const std::function& last_grounding_location /*= std::nullopt*/) : +Node::Node(const Point& p, const std::optional& last_grounding_location /*= std::nullopt*/) : m_is_root(true), m_p(p), m_last_grounding_location(last_grounding_location) {} -LightningTreeNodeSPtr LightningTreeNode::deepCopy() const +NodeSPtr Node::deepCopy() const { - LightningTreeNodeSPtr local_root = LightningTreeNode::create(m_p); + NodeSPtr local_root = Node::create(m_p); local_root->m_is_root = m_is_root; if (m_is_root) { @@ -99,14 +99,14 @@ LightningTreeNodeSPtr LightningTreeNode::deepCopy() const local_root->m_children.reserve(m_children.size()); for (const auto& node : m_children) { - LightningTreeNodeSPtr child = node->deepCopy(); + NodeSPtr child = node->deepCopy(); child->m_parent = local_root; local_root->m_children.push_back(child); } return local_root; } -void LightningTreeNode::reroot(LightningTreeNodeSPtr new_parent /*= nullptr*/) +void Node::reroot(NodeSPtr new_parent /*= nullptr*/) { if (! m_is_root) { auto old_parent = m_parent.lock(); @@ -124,13 +124,13 @@ void LightningTreeNode::reroot(LightningTreeNodeSPtr new_parent /*= nullptr*/) } } -LightningTreeNodeSPtr LightningTreeNode::closestNode(const Point& loc) +NodeSPtr Node::closestNode(const Point& loc) { - LightningTreeNodeSPtr result = shared_from_this(); + NodeSPtr result = shared_from_this(); auto closest_dist2 = coord_t((m_p - loc).cast().norm()); for (const auto& child : m_children) { - LightningTreeNodeSPtr candidate_node = child->closestNode(loc); + NodeSPtr candidate_node = child->closestNode(loc); const auto child_dist2 = coord_t((candidate_node->m_p - loc).cast().norm()); if (child_dist2 < closest_dist2) { closest_dist2 = child_dist2; @@ -183,7 +183,7 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG return visitor.d2min < within_max_dist * within_max_dist; } -bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) +bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) { if (outlines.empty()) return false; @@ -192,10 +192,10 @@ bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid& // Only keep children that have an unbroken connection to here, realign will put the rest in rerooted parts due to recursion: Point coll; bool reground_me = false; - m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const LightningTreeNodeSPtr &child) { + m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&](const NodeSPtr &child) { bool connect_branch = child->realign(outlines, outline_locator, rerooted_parts); // Find an intersection of the line segment from p to child->p, at maximum outline_locator.resolution() * 2 distance from p. - if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outlines, outline_locator, coll, outline_locator.resolution() * 2)) { + if (connect_branch && lineSegmentPolygonsIntersection(child->m_p, m_p, outline_locator, coll, outline_locator.resolution() * 2)) { child->m_last_grounding_location.reset(); child->m_parent.reset(); child->m_is_root = true; @@ -223,12 +223,12 @@ bool LightningTreeNode::realign(const Polygons& outlines, const EdgeGrid::Grid& return false; } -void LightningTreeNode::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) +void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) { straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist); } -LightningTreeNode::RectilinearJunction LightningTreeNode::straighten( +Node::RectilinearJunction Node::straighten( const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, @@ -259,7 +259,7 @@ LightningTreeNode::RectilinearJunction LightningTreeNode::straighten( constexpr coord_t close_enough = 10; child_p = m_children.front(); //recursive call to straighten might have removed the child - const LightningTreeNodeSPtr& parent_node = m_parent.lock(); + const NodeSPtr& parent_node = m_parent.lock(); if (parent_node && (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) { @@ -306,7 +306,7 @@ LightningTreeNode::RectilinearJunction LightningTreeNode::straighten( } // Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. -coord_t LightningTreeNode::prune(const coord_t& pruning_distance) +coord_t Node::prune(const coord_t& pruning_distance) { if (pruning_distance <= 0) return 0; @@ -343,7 +343,7 @@ coord_t LightningTreeNode::prune(const coord_t& pruning_distance) return max_distance_pruned; } -void LightningTreeNode::convertToPolylines(Polygons& output, const coord_t line_width) const +void Node::convertToPolylines(Polygons& output, const coord_t line_width) const { Polygons result; output.emplace_back(); @@ -352,7 +352,7 @@ void LightningTreeNode::convertToPolylines(Polygons& output, const coord_t line_ append(output, std::move(result)); } -void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& output) const +void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const { if (m_children.empty()) { output[long_line_idx].points.push_back(m_p); @@ -364,7 +364,7 @@ void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& outpu for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) { size_t child_idx = (first_child_idx + idx_offset) % m_children.size(); - const LightningTreeNode& child = *m_children[child_idx]; + const Node& child = *m_children[child_idx]; output.emplace_back(); size_t child_line_idx = output.size() - 1; child.convertToPolylines(child_line_idx, output); @@ -372,7 +372,7 @@ void LightningTreeNode::convertToPolylines(size_t long_line_idx, Polygons& outpu } } -void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const +void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const { const coord_t reduction = line_width / 2; // TODO make configurable? for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) { @@ -406,3 +406,5 @@ void LightningTreeNode::removeJunctionOverlap(Polygons& result_lines, const coor ++ poly_it; } } + +} // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index 1ca4b01d6a..bc2e76dc2f 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -12,14 +12,14 @@ #include "../../EdgeGrid.hpp" #include "../../Polygon.hpp" -namespace Slic3r +namespace Slic3r::FillLightning { constexpr auto locator_cell_size = scaled(4.); -class LightningTreeNode; +class Node; -using LightningTreeNodeSPtr = std::shared_ptr; +using NodeSPtr = std::shared_ptr; // NOTE: As written, this struct will only be valid for a single layer, will have to be updated for the next. // NOTE: Reasons for implementing this with some separate closures: @@ -35,15 +35,15 @@ using LightningTreeNodeSPtr = std::shared_ptr; * a tree. The class also has some helper functions specific to Lightning Infill * e.g. to straighten the paths around this node. */ -class LightningTreeNode : public std::enable_shared_from_this +class Node : public std::enable_shared_from_this { public: // Workaround for private/protected constructors and 'make_shared': https://stackoverflow.com/a/27832765 - template LightningTreeNodeSPtr static create(Arg&&...arg) + template NodeSPtr static create(Arg&&...arg) { - struct EnableMakeShared : public LightningTreeNode + struct EnableMakeShared : public Node { - EnableMakeShared(Arg&&...arg) : LightningTreeNode(std::forward(arg)...) {} + EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {} }; return std::make_shared(std::forward(arg)...); } @@ -62,19 +62,19 @@ public: void setLocation(const Point& p) { m_p = p; } /*! - * Construct a new ``LightningTreeNode`` instance and add it as a child of + * Construct a new ``Node`` instance and add it as a child of * this node. * \param p The location of the new node. * \return A shared pointer to the new node. */ - LightningTreeNodeSPtr addChild(const Point& p); + NodeSPtr addChild(const Point& p); /*! - * Add an existing ``LightningTreeNode`` as a child of this node. + * Add an existing ``Node`` as a child of this node. * \param new_child The node that must be added as a child. * \return Always returns \p new_child. */ - LightningTreeNodeSPtr addChild(LightningTreeNodeSPtr& new_child); + NodeSPtr addChild(NodeSPtr& new_child); /*! * Propagate this node's sub-tree to the next layer. @@ -96,7 +96,7 @@ public: */ void propagateToNextLayer ( - std::vector& next_trees, + std::vector& next_trees, const Polygons& next_outlines, const EdgeGrid::Grid& outline_locator, const coord_t prune_distance, @@ -127,7 +127,7 @@ public: * \param visitor A function to execute for every node in this node's sub- * tree. */ - void visitNodes(const std::function& visitor); + void visitNodes(const std::function& visitor); /*! * Get a weighted distance from an unsupported point to this node (given the current supporting radius). @@ -156,14 +156,14 @@ public: * This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf. * \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree. */ - void reroot(LightningTreeNodeSPtr new_parent = nullptr); + void reroot(NodeSPtr new_parent = nullptr); /*! * Retrieves the closest node to the specified location. * \param loc The specified location. * \result The branch that starts at the position closest to the location within this tree. */ - LightningTreeNodeSPtr closestNode(const Point& loc); + NodeSPtr closestNode(const Point& loc); /*! * Returns whether the given tree node is a descendant of this node. @@ -174,10 +174,10 @@ public: * \return ``true`` if the given node is a descendant or this node itself, * or ``false`` if it is not in the sub-tree. */ - bool hasOffspring(const LightningTreeNodeSPtr& to_be_checked) const; + bool hasOffspring(const NodeSPtr& to_be_checked) const; protected: - LightningTreeNode() = delete; // Don't allow empty contruction + Node() = delete; // Don't allow empty contruction /*! * Construct a new node, either for insertion in a tree or as root. @@ -185,19 +185,19 @@ protected: * Connecting other nodes to this node indicates that a line segment should * be drawn between those two physical positions. */ - LightningTreeNode(const Point& p, const std::optional& last_grounding_location = std::nullopt); + Node(const Point& p, const std::optional& last_grounding_location = std::nullopt); /*! * Copy this node and its entire sub-tree. * \return The equivalent of this node in the copy (the root of the new sub- * tree). */ - LightningTreeNodeSPtr deepCopy() const; + NodeSPtr deepCopy() const; /*! Reconnect trees from the layer above to the new outlines of the lower layer. * \return Wether or not the root is kept (false is no, true is yes). */ - bool realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts); + bool realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts); struct RectilinearJunction { @@ -261,15 +261,15 @@ protected: bool m_is_root; Point m_p; - std::weak_ptr m_parent; - std::vector m_children; + std::weak_ptr m_parent; + std::vector m_children; std::optional m_last_grounding_location; //enum_values.push_back("octagramspiral"); def->enum_values.push_back("adaptivecubic"); def->enum_values.push_back("supportcubic"); +#if HAS_LIGHTNING_INFILL + def->enum_values.push_back("lightning"); +#endif // HAS_LIGHTNING_INFILL def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Aligned Rectilinear")); def->enum_labels.push_back(L("Grid")); @@ -1151,6 +1157,9 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Octagram Spiral")); def->enum_labels.push_back(L("Adaptive Cubic")); def->enum_labels.push_back(L("Support Cubic")); +#if HAS_LIGHTNING_INFILL + def->enum_labels.push_back(L("Lightning")); +#endif // HAS_LIGHTNING_INFILL def->set_default_value(new ConfigOptionEnum(ipStars)); def = this->add("first_layer_acceleration", coFloat); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8d3ab3720c..6851ceb106 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -57,9 +57,15 @@ enum class FuzzySkinType { All, }; +#define HAS_LIGHTNING_INFILL 0 + enum InfillPattern : int { ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, ipCount, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, +#if HAS_LIGHTNING_INFILL + ipLightning, +#endif // HAS_LIGHTNING_INFILL +ipCount, }; enum class IroningType { From 9cf483fe0822caf432f86edde38db8f423ceeae5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 1 Dec 2021 19:02:37 +0100 Subject: [PATCH 42/50] WIP Lightning infil: Cleaning up some compiler errors --- src/libslic3r/Fill/Lightning/DistanceField.hpp | 2 +- src/libslic3r/Fill/Lightning/Generator.cpp | 2 +- src/libslic3r/Fill/Lightning/Layer.cpp | 4 ++-- src/libslic3r/Layer.hpp | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index 6e550a7e25..efb073a0d2 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -73,7 +73,7 @@ protected: */ struct UnsupportedCell { - UnsupportedCell(Point grid_loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} + UnsupportedCell(Point loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} // The position of the center of this cell. Point loc; // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index 5d935b1042..87b593f89b 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -32,7 +32,7 @@ Generator::Generator(const PrintObject &print_object) const PrintRegionConfig ®ion_config = print_object.shared_regions()->all_regions.front()->config(); const std::vector &nozzle_diameters = print_config.nozzle_diameter.values; double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); - const int infill_extruder = region_config.infill_extruder.value; +// const int infill_extruder = region_config.infill_extruder.value; const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. const double layer_thickness = object_config.layer_height; diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 19bf7ec481..1b3936e340 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -193,7 +193,7 @@ void Layer::reconnectRoots if (root_ptr->getLastGroundingLocation()) { - const Point& ground_loc = root_ptr->getLastGroundingLocation().value(); + const Point& ground_loc = *root_ptr->getLastGroundingLocation(); if (ground_loc != root_ptr->getLocation()) { Point new_root_pt; @@ -225,7 +225,7 @@ void Layer::reconnectRoots ); if (ground.boundary_location) { - if (ground.boundary_location.value() == root_ptr->getLocation()) + if (*ground.boundary_location == root_ptr->getLocation()) continue; // Already on the boundary. auto new_root = Node::create(ground.p(), ground.p()); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 60440de97f..bb652d8a04 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -148,7 +148,6 @@ public: return false; } void make_perimeters(); - void make_fills() { this->make_fills(nullptr, nullptr); }; void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree); void make_ironing(); From 9ea81a9dbfcde165b5dab56bf4c8c1f07825c4d1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 1 Dec 2021 19:24:23 +0100 Subject: [PATCH 43/50] Fixing Perl unit tests --- src/libslic3r/Layer.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index bb652d8a04..516b6da9b1 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -148,6 +148,8 @@ public: return false; } void make_perimeters(); + // Phony version of make_fills() without parameters for Perl integration only. + void make_fills() { this->make_fills(nullptr, nullptr); } void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree); void make_ironing(); From ba20cc489269b730a6fe017d414ed8448cefc28a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Dec 2021 09:09:19 +0100 Subject: [PATCH 44/50] #7396 - Fix of crash while slicing a particular stl file (missing checks into GCodeViewer::load_toolpaths()) --- src/slic3r/GUI/GCodeViewer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 69e652d4f1..cdc9c33a4f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1190,7 +1190,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) vertices.push_back(normal.z()); }; - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr, vbuffer_id, vertices.size(), move_id - 1); buffer.paths.back().sub_paths.back().first.position = prev.position; } @@ -1281,7 +1281,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) store_triangle(indices, v_offsets[4], v_offsets[5], v_offsets[6]); }; - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1); buffer.paths.back().sub_paths.back().first.position = prev.position; } From e85a0ba248829c8d0dcc4f6aec6a39685f451cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 2 Dec 2021 09:30:03 +0100 Subject: [PATCH 45/50] Added missing includes (GCC11.1 without PCH). --- src/libslic3r/Fill/FillBase.hpp | 2 +- src/libslic3r/Fill/FillLightning.cpp | 1 + src/libslic3r/Fill/Lightning/DistanceField.cpp | 1 + src/libslic3r/Fill/Lightning/DistanceField.hpp | 3 +++ src/libslic3r/Fill/Lightning/Layer.hpp | 1 + src/libslic3r/Fill/Lightning/TreeNode.cpp | 1 + 6 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index a881ce611c..6c57a64bfa 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -13,10 +13,10 @@ #include "../BoundingBox.hpp" #include "../Exception.hpp" #include "../Utils.hpp" +#include "../ExPolygon.hpp" namespace Slic3r { -class ExPolygon; class Surface; enum InfillPattern : int; diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp index 52c3ce4c59..447fd8057b 100644 --- a/src/libslic3r/Fill/FillLightning.cpp +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -2,6 +2,7 @@ #include "FillLightning.hpp" #include "Lightning/Generator.hpp" +#include "../Surface.hpp" #include #include diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 125da92228..6df1d043ca 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -3,6 +3,7 @@ #include "DistanceField.hpp" //Class we're implementing. #include "../FillRectilinear.hpp" +#include "../../ClipperUtils.hpp" namespace Slic3r::FillLightning { diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index efb073a0d2..7fdce434ce 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -4,6 +4,9 @@ #ifndef LIGHTNING_DISTANCE_FIELD_H #define LIGHTNING_DISTANCE_FIELD_H +#include "../../Point.hpp" +#include "../../Polygon.hpp" + namespace Slic3r::FillLightning { diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index 7ef5eaec19..853c634f27 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Slic3r::FillLightning { diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 2cb2b5f6d4..8ab11bd6d4 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -4,6 +4,7 @@ #include "TreeNode.hpp" #include "../../Geometry.hpp" +#include "../../ClipperUtils.hpp" namespace Slic3r::FillLightning { From 7f951c2df8e0b654c08c118502c8378c05fde435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 1 Dec 2021 08:18:27 +0100 Subject: [PATCH 46/50] Fixed an issue that travels could lead through a hole when the avoid crossing perimeters was enabled at the same with "External perimeter first" or "Detect thin walls". --- src/libslic3r/GCode/AvoidCrossingPerimeters.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 1f538862b0..d29f7216a5 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -254,7 +254,7 @@ static std::vector extend_for_closest_lines(const std::vector new_intersections = intersections; - if (!intersections.empty() && !start_lines.empty()) { + if (!new_intersections.empty() && !start_lines.empty()) { size_t cl_start_idx = get_closer(start_lines, new_intersections.front(), start); if (cl_start_idx != std::numeric_limits::max()) { // If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine. @@ -265,11 +265,13 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? start_lines[start_closest_lines_idx] : start_lines.front(); new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}); } - } else if (!intersections.empty() && !end_lines.empty()) { + } + + if (!new_intersections.empty() && !end_lines.empty()) { size_t cl_end_idx = get_closer(end_lines, new_intersections.back(), end); if (cl_end_idx != std::numeric_limits::max()) { // If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine. @@ -280,7 +282,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? end_lines[end_closest_lines_idx] : end_lines.front(); new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}); } From 9788a80c6f12765fce23fd81789242b66624854a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 30 Nov 2021 12:42:09 +0100 Subject: [PATCH 47/50] Added port number to resolved ip address if specified. fix of #7361 --- src/slic3r/Utils/OctoPrint.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index b084b67571..58431691cf 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -115,8 +115,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro std::string url; bool res = true; - if (m_host.find("https://") == 0 || test_msg.empty()) - { + if (m_host.find("https://") == 0 || test_msg.empty()) { // If https is entered we assume signed ceritificate is being used // IP resolving will not happen - it could resolve into address not being specified in cert url = make_url("api/files/local"); @@ -129,6 +128,34 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro // put ipv6 into [] brackets (there shouldn't be any http:// if its resolved addr) if (resolved_addr.find(':') != std::string::npos && resolved_addr.at(0) != '[') resolved_addr = "[" + resolved_addr + "]"; + // port number is not part of resolved addr. If it is in m_host, we need to add it again + // if ipv6 the format would be [add]:port + if (size_t port_start = m_host.rfind(':'); port_start != std::string::npos) { + size_t count_of_colon = std::count(m_host.begin(), m_host.end(), ':'); + // ipv6 + if (size_t addr_end = m_host.rfind(']'); addr_end != port_start - 1 && count_of_colon > 2) + port_start = std::string::npos; + // http:// (https cant go to this else branch) + else if (m_host.find("http:") == 0 && port_start == 4) + port_start = std::string::npos; + // add port to resolved addres + if (port_start != std::string::npos && port_start < m_host.size()) { + std::string port_string = m_host.substr(port_start); + // last check - try casting port string to number. + bool cont = true; + for (size_t i = 1; i < port_string.size(); i++) { + // number smaller than 65535 + if (port_string[i] < '0' || port_string[i] > '9' || i > 6) { + cont = false; + BOOST_LOG_TRIVIAL(debug) << "IP resolve wrongly concidered string as port: " << port_string; + break; + } + } + if (cont) + resolved_addr += port_string; + + } + } url = make_url("api/files/local", resolved_addr); } From 0939dab807cc0aa8aa4ddb44e9c4a31f43e8b86b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 30 Nov 2021 12:42:59 +0100 Subject: [PATCH 48/50] allow_ip_resolve in app config --- src/libslic3r/AppConfig.cpp | 3 +++ src/slic3r/Utils/OctoPrint.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 9c9e7e0f30..ab636a03ea 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -174,6 +174,9 @@ void AppConfig::set_defaults() if (get("show_hints").empty()) set("show_hints", "1"); + if (get("allow_ip_resolve").empty()) + set("allow_ip_resolve", "1"); + #ifdef _WIN32 if (get("use_legacy_3DConnexion").empty()) set("use_legacy_3DConnexion", "0"); diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 58431691cf..df2e0d2b17 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -15,6 +15,7 @@ #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/GUI.hpp" #include "Http.hpp" +#include "libslic3r/AppConfig.hpp" namespace fs = boost::filesystem; @@ -115,7 +116,9 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro std::string url; bool res = true; - if (m_host.find("https://") == 0 || test_msg.empty()) { + bool allow_ip_resolve = GUI::get_app_config()->get("allow_ip_resolve") == "1"; + + if (m_host.find("https://") == 0 || test_msg.empty() || !allow_ip_resolve) { // If https is entered we assume signed ceritificate is being used // IP resolving will not happen - it could resolve into address not being specified in cert url = make_url("api/files/local"); From a4b4af8a1acda308257906cbdac95f6d2d47492a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 2 Dec 2021 10:25:21 +0100 Subject: [PATCH 49/50] Change of 71082adbc9ac2101c39f46218d666e46ab4ecb7c Instead of just adding port, take original address and replace just host with resolved host. fix of #7389 --- src/slic3r/Utils/OctoPrint.cpp | 59 +++++++++++++++++----------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index df2e0d2b17..0aec912c1d 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -24,6 +24,30 @@ namespace pt = boost::property_tree; namespace Slic3r { +namespace { +std::string substitute_host(const std::string& orig_addr, const std::string sub_addr) +{ + //URI = scheme ":"["//"[userinfo "@"] host [":" port]] path["?" query]["#" fragment] + std::string final_addr = orig_addr; + // http + size_t double_dash = orig_addr.find("//"); + size_t host_start = (double_dash == std::string::npos ? 0 : double_dash + 2); + // userinfo + size_t at = orig_addr.find("@"); + host_start = (at != std::string::npos && at > host_start ? at + 1 : host_start); + // end of host, could be port, subpath (could be query or fragment?) + size_t host_end = orig_addr.find_first_of(":/?#", host_start); + host_end = (host_end == std::string::npos ? orig_addr.length() : host_end); + // now host_start and host_end should mark where to put resolved addr + // check host_start. if its nonsense, lets just use original addr (or resolved addr?) + if (host_start >= orig_addr.length()) { + return final_addr; + } + final_addr.replace(host_start, host_end - host_start, sub_addr); + return final_addr; +} +} //namespace + OctoPrint::OctoPrint(DynamicPrintConfig *config) : m_host(config->opt_string("print_host")), m_apikey(config->opt_string("printhost_apikey")), @@ -128,38 +152,13 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro // This new address returns in "test_msg" variable. // Solves troubles of uploades failing with name address. std::string resolved_addr = boost::nowide::narrow(test_msg); - // put ipv6 into [] brackets (there shouldn't be any http:// if its resolved addr) + // put ipv6 into [] brackets if (resolved_addr.find(':') != std::string::npos && resolved_addr.at(0) != '[') resolved_addr = "[" + resolved_addr + "]"; - // port number is not part of resolved addr. If it is in m_host, we need to add it again - // if ipv6 the format would be [add]:port - if (size_t port_start = m_host.rfind(':'); port_start != std::string::npos) { - size_t count_of_colon = std::count(m_host.begin(), m_host.end(), ':'); - // ipv6 - if (size_t addr_end = m_host.rfind(']'); addr_end != port_start - 1 && count_of_colon > 2) - port_start = std::string::npos; - // http:// (https cant go to this else branch) - else if (m_host.find("http:") == 0 && port_start == 4) - port_start = std::string::npos; - // add port to resolved addres - if (port_start != std::string::npos && port_start < m_host.size()) { - std::string port_string = m_host.substr(port_start); - // last check - try casting port string to number. - bool cont = true; - for (size_t i = 1; i < port_string.size(); i++) { - // number smaller than 65535 - if (port_string[i] < '0' || port_string[i] > '9' || i > 6) { - cont = false; - BOOST_LOG_TRIVIAL(debug) << "IP resolve wrongly concidered string as port: " << port_string; - break; - } - } - if (cont) - resolved_addr += port_string; - - } - } - url = make_url("api/files/local", resolved_addr); + // in original address (m_host) replace host for resolved ip + std::string final_addr = substitute_host(m_host, resolved_addr); + BOOST_LOG_TRIVIAL(debug) << "Upload address after ip resolve: " << final_addr; + url = make_url("api/files/local", final_addr); } BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%") From 48d88191e2a61ba0b3693989432b2d6108d3786d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Dec 2021 11:11:00 +0100 Subject: [PATCH 50/50] Fixed rotation of volumes while ALT key is pressed --- src/slic3r/GUI/Selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c596b0c2c2..12c26537c3 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -804,7 +804,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ rotate_instance(v, i); else if (is_single_volume() || is_single_modifier()) { if (transformation_type.independent()) - v.set_volume_rotation(v.get_volume_rotation() + rotation); + v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation); else { const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());