diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index f7f6bb2f99..ac4af48283 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -720,28 +720,26 @@ void Transformation::reset() } #if ENABLE_WORLD_COORDINATE +void Transformation::reset_rotation() +{ + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.v * svd.s * svd.v.transpose()) * svd.mirror_matrix(); +} + +void Transformation::reset_scaling_factor() +{ + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); +} + void Transformation::reset_skew() { - Matrix3d rotation; - Matrix3d scale; - m_matrix.computeRotationScaling(&rotation, &scale); + auto new_scale_factor = [](const Matrix3d& s) { + return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average + }; - const double average_scale = std::cbrt(scale(0, 0) * scale(1, 1) * scale(2, 2)); - - scale(0, 0) = is_left_handed() ? -average_scale : average_scale; - scale(1, 1) = average_scale; - scale(2, 2) = average_scale; - - scale(0, 1) = 0.0; - scale(0, 2) = 0.0; - scale(1, 0) = 0.0; - scale(1, 2) = 0.0; - scale(2, 0) = 0.0; - scale(2, 1) = 0.0; - - const Vec3d offset = get_offset(); - m_matrix = rotation * scale; - m_matrix.translation() = offset; + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); } Transform3d Transformation::get_matrix_no_offset() const @@ -838,6 +836,43 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation } #endif // !ENABLE_WORLD_COORDINATE +#if ENABLE_WORLD_COORDINATE +TransformationSVD::TransformationSVD(const Transform3d& trafo) +{ + const auto &m0 = trafo.matrix().block<3, 3>(0, 0); + mirror = m0.determinant() < 0.0; + + Matrix3d m; + if (mirror) + m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); + else + m = m0; + const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); + u = svd.matrixU(); + v = svd.matrixV(); + s = svd.singularValues().asDiagonal(); + + scale = !s.isApprox(Matrix3d::Identity()); + anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); + rotation = !v.isApprox(u); + + if (anisotropic_scale) { + rotation_90_degrees = true; + for (int i = 0; i < 3; ++i) { + const Vec3d row = v.row(i).cwiseAbs(); + size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + if (num_zeros != 2 || num_ones != 1) { + rotation_90_degrees = false; + break; + } + } + skew = ! rotation_90_degrees; + } else + skew = false; +} +#endif // ENABLE_WORLD_COORDINATE + // For parsing a transformation matrix from 3MF / AMF. Transform3d transform3d_from_string(const std::string& transform_str) { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index aa37d94dd2..1f287f6a7f 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -492,8 +492,8 @@ public: void reset(); #if ENABLE_WORLD_COORDINATE void reset_offset() { set_offset(Vec3d::Zero()); } - void reset_rotation() { set_rotation(Vec3d::Zero()); } - void reset_scaling_factor() { set_scaling_factor(Vec3d::Ones()); } + void reset_rotation(); + void reset_scaling_factor(); void reset_mirror() { set_mirror(Vec3d::Ones()); } void reset_skew(); @@ -538,6 +538,27 @@ private: #endif // ENABLE_WORLD_COORDINATE }; +#if ENABLE_WORLD_COORDINATE +struct TransformationSVD +{ + Matrix3d u = Matrix3d::Identity(); + Matrix3d s = Matrix3d::Identity(); + Matrix3d v = Matrix3d::Identity(); + + bool mirror{ false }; + bool scale{ false }; + bool anisotropic_scale{ false }; + bool rotation{ false }; + bool rotation_90_degrees{ false }; + bool skew{ false }; + + explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {} + explicit TransformationSVD(const Transform3d& trafo); + + Eigen::DiagonalMatrix mirror_matrix() const { return Eigen::DiagonalMatrix(this->mirror ? -1. : 1., 1., 1.); } +}; +#endif // ENABLE_WORLD_COORDINATE + // For parsing a transformation matrix from 3MF / AMF. extern Transform3d transform3d_from_string(const std::string& transform_str); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 48e614a39f..a76fbb764b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3947,8 +3947,8 @@ void GLCanvas3D::update_sequential_clearance() // the results are then cached for following displacements if (m_sequential_print_clearance_first_displacement) { m_sequential_print_clearance.m_hull_2d_cache.clear(); - float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); - double mitter_limit = scale_(0.1); + const float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); + const double mitter_limit = scale_(0.1); m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); for (size_t i = 0; i < m_model->objects.size(); ++i) { ModelObject* model_object = m_model->objects[i]; @@ -3956,7 +3956,7 @@ void GLCanvas3D::update_sequential_clearance() #if ENABLE_WORLD_COORDINATE Geometry::Transformation trafo = model_instance0->get_transformation(); trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() }); - Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), + const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. shrink_factor, @@ -3984,13 +3984,8 @@ void GLCanvas3D::update_sequential_clearance() polygons.reserve(instances_count); for (size_t i = 0; i < instance_transforms.size(); ++i) { const auto& instances = instance_transforms[i]; - double rotation_z0 = instances.front()->get_rotation().z(); for (const auto& instance : instances) { - Geometry::Transformation transformation; - const Vec3d& offset = instance->get_offset(); - transformation.set_offset({ offset.x(), offset.y(), 0.0 }); - transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0); - const Transform3d& trafo = transformation.get_matrix(); + const Transform3d& trafo = instance->get_matrix(); const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i]; Points inst_pts; inst_pts.reserve(hull_2d.size()); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 0375a3ce71..6d4c916f19 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -448,14 +448,21 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : Selection& selection = canvas->get_selection(); #if ENABLE_WORLD_COORDINATE - if (selection.is_single_volume_or_modifier()) + if (selection.is_single_volume_or_modifier()) { + GLVolume* vol = const_cast(selection.get_first_volume()); + Geometry::Transformation trafo = vol->get_volume_transformation(); + trafo.reset_rotation(); + vol->set_volume_transformation(trafo); + } #else if (selection.is_single_volume() || selection.is_single_modifier()) -#endif // ENABLE_WORLD_COORDINATE const_cast(selection.get_first_volume())->set_volume_rotation(Vec3d::Zero()); +#endif // ENABLE_WORLD_COORDINATE else if (selection.is_single_full_instance()) { + Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation(); + trafo.reset_rotation(); for (unsigned int idx : selection.get_volume_idxs()) { - const_cast(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero()); + const_cast(selection.get_volume(idx))->set_instance_transformation(trafo); } } else @@ -484,24 +491,22 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); if (selection.is_single_volume_or_modifier()) { - const bool is_left_handed = selection.get_first_volume()->get_volume_transformation().is_left_handed(); - const_cast(selection.get_first_volume())->set_volume_scaling_factor(Vec3d::Ones()); - if (is_left_handed) - const_cast(selection.get_first_volume())->set_volume_mirror({ -1.0 , 1.0, 1.0 }); + GLVolume* vol = const_cast(selection.get_first_volume()); + Geometry::Transformation trafo = vol->get_volume_transformation(); + trafo.reset_scaling_factor(); + vol->set_volume_transformation(trafo); } else if (selection.is_single_full_instance()) { - const bool is_left_handed = selection.get_first_volume()->get_instance_transformation().is_left_handed(); + Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation(); + trafo.reset_scaling_factor(); for (unsigned int idx : selection.get_volume_idxs()) { - const_cast(selection.get_volume(idx))->set_instance_scaling_factor(Vec3d::Ones()); - if (is_left_handed) - const_cast(selection.get_volume(idx))->set_instance_mirror({ -1.0 , 1.0, 1.0 }); + const_cast(selection.get_volume(idx))->set_instance_transformation(trafo); } } else return; canvas->do_scale(L("Reset scale")); - UpdateAndShow(true); #else Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale")); @@ -740,7 +745,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_rotate_label_string = L("Rotate (relative)"); m_new_position = Vec3d::Zero(); m_new_rotation = Vec3d::Zero(); - m_new_scale = Vec3d(100.0, 100.0, 100.0); m_new_size = selection.get_bounding_box_in_current_reference_system().first.size(); #else m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); @@ -927,93 +931,48 @@ void ObjectManipulation::update_if_dirty() +#if ENABLE_WORLD_COORDINATE void ObjectManipulation::update_reset_buttons_visibility() { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); if (!canvas) return; - const Selection& selection = canvas->get_selection(); + bool show_drop_to_bed = false; bool show_rotation = false; bool show_scale = false; - bool show_drop_to_bed = false; -#if ENABLE_WORLD_COORDINATE + bool show_mirror = false; bool show_skew = false; - bool show_mirror_warning = false; + const Selection& selection = canvas->get_selection(); if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() : get_volume_min_z(*selection.get_first_volume()); show_drop_to_bed = std::abs(min_z) > EPSILON; const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo; -#else - if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { - const GLVolume* volume = selection.get_first_volume(); - Vec3d rotation; - Vec3d scale; - double min_z = 0.0; -#endif // ENABLE_WORLD_COORDINATE + const Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation(); - if (selection.is_single_full_instance()) { -#if ENABLE_WORLD_COORDINATE - trafo = volume->get_instance_transformation(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int id : idxs) { - const Geometry::Transformation world_trafo(selection.get_volume(id)->world_matrix()); - show_skew |= world_trafo.has_skew(); - show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0; - } -#else - rotation = volume->get_instance_rotation(); - scale = volume->get_instance_scaling_factor(); - min_z = selection.get_scaled_instance_bounding_box().min.z(); -#endif // ENABLE_WORLD_COORDINATE - } - else { -#if ENABLE_WORLD_COORDINATE - Geometry::Transformation trafo = volume->get_volume_transformation(); - const Geometry::Transformation world_trafo(volume->world_matrix()); - show_skew |= world_trafo.has_skew(); - show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0; -#else - rotation = volume->get_volume_rotation(); - scale = volume->get_volume_scaling_factor(); - min_z = get_volume_min_z(*volume); -#endif // ENABLE_WORLD_COORDINATE - } -#if ENABLE_WORLD_COORDINATE - const Transform3d rotation = trafo.get_rotation_matrix(); - const Transform3d scale = trafo.get_scaling_factor_matrix(); - show_rotation = show_mirror_warning ? !trafo.get_matrix().matrix().block<3, 3>(0, 0).isDiagonal() : !rotation.isApprox(Transform3d::Identity()); - show_scale = !scale.isApprox(Transform3d::Identity()); -#else - show_rotation = !rotation.isApprox(Vec3d::Zero()); - show_scale = !scale.isApprox(Vec3d::Ones()); - show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; -#endif // ENABLE_WORLD_COORDINATE + const Geometry::TransformationSVD trafo_svd(trafo); + show_rotation = trafo_svd.rotation; + show_scale = trafo_svd.scale; + show_mirror = trafo_svd.mirror; + show_skew = trafo_svd.skew; } -#if ENABLE_WORLD_COORDINATE - wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed, show_skew, show_mirror_warning] { -#else - wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { -#endif // ENABLE_WORLD_COORDINATE + wxGetApp().CallAfter([this, show_drop_to_bed, show_rotation, show_scale, show_mirror, show_skew] { // There is a case (under OSX), when this function is called after the Manipulation panel is hidden // So, let check if Manipulation panel is still shown for this moment if (!this->IsShown()) return; + m_drop_to_bed_button->Show(show_drop_to_bed); m_reset_rotation_button->Show(show_rotation); m_reset_scale_button->Show(show_scale); - m_drop_to_bed_button->Show(show_drop_to_bed); -#if ENABLE_WORLD_COORDINATE + m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap); + m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0)); + m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : ""); m_reset_skew_button->Show(show_skew); m_skew_label->Show(show_skew); - m_mirror_warning_bitmap->SetBitmap(show_mirror_warning ? m_manifold_warning_bmp.bmp() : wxNullBitmap); - m_mirror_warning_bitmap->SetMinSize(show_mirror_warning ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0)); - m_mirror_warning_bitmap->SetToolTip(show_mirror_warning ? _L("Left handed") : ""); -#endif // ENABLE_WORLD_COORDINATE // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time Sidebar& panel = wxGetApp().sidebar(); @@ -1024,23 +983,75 @@ void ObjectManipulation::update_reset_buttons_visibility() } }); } +#else +void ObjectManipulation::update_reset_buttons_visibility() +{ + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + if (!canvas) + return; + const Selection& selection = canvas->get_selection(); + + bool show_rotation = false; + 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_first_volume(); + Vec3d rotation; + Vec3d scale; + double min_z = 0.0; + + if (selection.is_single_full_instance()) { + rotation = volume->get_instance_rotation(); + scale = volume->get_instance_scaling_factor(); + min_z = selection.get_scaled_instance_bounding_box().min.z(); + } + else { + rotation = volume->get_volume_rotation(); + scale = volume->get_volume_scaling_factor(); + min_z = get_volume_min_z(*volume); + } + show_rotation = !rotation.isApprox(Vec3d::Zero()); + show_scale = !scale.isApprox(Vec3d::Ones()); + show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; + } + + wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { + // There is a case (under OSX), when this function is called after the Manipulation panel is hidden + // So, let check if Manipulation panel is still shown for this moment + if (!this->IsShown()) + return; + m_reset_rotation_button->Show(show_rotation); + m_reset_scale_button->Show(show_scale); + m_drop_to_bed_button->Show(show_drop_to_bed); + + // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time + Sidebar& panel = wxGetApp().sidebar(); + if (!panel.IsFrozen()) { + panel.Freeze(); + panel.Layout(); + panel.Thaw(); + } + }); +} +#endif // ENABLE_WORLD_COORDINATE void ObjectManipulation::update_mirror_buttons_visibility() { +#if ENABLE_WORLD_COORDINATE + const bool can_mirror = wxGetApp().plater()->can_mirror(); + for (ScalableButton* button : m_mirror_buttons) { + button->Enable(can_mirror); + } +#else GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); -#if ENABLE_WORLD_COORDINATE - if (is_local_coordinates()) { - if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { -#else std::array new_states = { mbHidden, mbHidden, mbHidden }; if (!m_world_coordinates) { if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { -#endif // ENABLE_WORLD_COORDINATE const GLVolume* volume = selection.get_first_volume(); Vec3d mirror; @@ -1049,19 +1060,10 @@ void ObjectManipulation::update_mirror_buttons_visibility() else mirror = volume->get_volume_mirror(); -#if !ENABLE_WORLD_COORDINATE for (unsigned char i=0; i<3; ++i) new_states[i] = (mirror[i] < 0. ? mbActive : mbShown); -#endif // !ENABLE_WORLD_COORDINATE } } - -#if ENABLE_WORLD_COORDINATE - const bool can_mirror = wxGetApp().plater()->can_mirror(); - for (ScalableButton* button : m_mirror_buttons) { - button->Enable(can_mirror); - } -#else else { // the mirroring buttons should be hidden in world coordinates, // unless we make it actually mirror in world coords. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 322c631514..a884a0589d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -198,16 +198,23 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const } void GLGizmoBase::render_grabbers(float size) const +{ + render_grabbers(0, m_grabbers.size() - 1, size, false); +} + +void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - for (int i = 0; i < (int)m_grabbers.size(); ++i) { + glsafe(::glDisable(GL_CULL_FACE)); + for (size_t i = first; i <= last; ++i) { if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i, size); + m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size); } + glsafe(::glEnable(GL_CULL_FACE)); shader->stop_using(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 38d8c26617..108b869da5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -219,6 +219,7 @@ protected: void render_grabbers(const BoundingBoxf3& box) const; void render_grabbers(float size) const; + void render_grabbers(size_t first, size_t last, float size, bool force_hover) const; std::string format(float value, unsigned int decimals) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 28cf8a0a31..194f9c6f83 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1127,14 +1127,14 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ(); if (starting_vec.norm() != 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) // 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 + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; + const Vec3d inters_vec = inters - starting_drag_position; starting_vec.normalize(); // finds projection of the vector along the staring direction diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 88803e59d4..947ef2df8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -3,7 +3,7 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" - +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" @@ -38,6 +38,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) // Rotate the object so the normal points downward: selection.flattening_rotate(m_planes[m_hover_id].normal); m_parent.do_rotate(L("Gizmo-Place on Face")); + wxGetApp().obj_manipul()->set_dirty(); } return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 7656226b59..98f183b74e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -339,7 +339,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - const 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; // vector from the starting position to the found intersection const Vec3d inters_vec = inters - m_starting_drag_position; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 345d733afd..a189600b1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -238,7 +238,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); - m_orient_matrix = box_trafo; + m_orient_matrix = Geometry::translation_transform(m_center); + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { + const GLVolume& v = *selection.get_first_volume(); + m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix(); + if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) + m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix(); + } m_radius = Offset + m_bounding_box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 173585ec00..9e5191f65f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -218,15 +218,7 @@ void GLGizmoScale3D::on_render() m_bounding_box = box; m_center = box_trafo.translation(); m_grabbers_transform = box_trafo; - m_instance_center = Vec3d::Zero(); - if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) - m_instance_center = selection.get_first_volume()->get_instance_offset(); - else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates()) - m_instance_center = m_center; - else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) - m_instance_center = m_center; - else - m_instance_center = selection.is_single_full_instance() ? selection.get_first_volume()->get_instance_offset() : m_center; + m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center; // x axis const Vec3d box_half_size = 0.5 * m_bounding_box.size(); @@ -264,9 +256,8 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); - const Transform3d base_matrix = local_transform(selection); for (int i = 0; i < 10; ++i) { - m_grabbers[i].matrix = base_matrix; + m_grabbers[i].matrix = m_grabbers_transform; } const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0); @@ -281,7 +272,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -315,7 +306,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -332,8 +323,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[0].render(true, grabber_mean_size); - m_grabbers[1].render(true, grabber_mean_size); + render_grabbers(0, 1, grabber_mean_size, true); shader->stop_using(); } } @@ -347,7 +337,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -364,8 +354,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[2].render(true, grabber_mean_size); - m_grabbers[3].render(true, grabber_mean_size); + render_grabbers(2, 3, grabber_mean_size, true); shader->stop_using(); } } @@ -379,7 +368,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -396,8 +385,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[4].render(true, grabber_mean_size); - m_grabbers[5].render(true, grabber_mean_size); + render_grabbers(4, 5, grabber_mean_size, true); shader->stop_using(); } } @@ -411,7 +399,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -431,9 +419,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - for (int i = 6; i < 10; ++i) { - m_grabbers[i].render(true, grabber_mean_size); - } + render_grabbers(6, 9, grabber_mean_size, true); shader->stop_using(); } } @@ -757,7 +743,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) double ratio = calc_ratio(data); if (ratio > 0.0) { Vec3d curr_scale = m_scale; - Vec3d starting_scale = m_starting.scale; + const Vec3d starting_scale = m_starting.scale; const Selection& selection = m_parent.get_selection(); const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); @@ -770,13 +756,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (m_hover_id == 2 * axis) local_offset *= -1.0; - Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection) - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from world coordinates to instance coordinates - center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset; - - local_offset += (ratio - 1.0) * center_offset(axis); - switch (axis) { case X: { m_offset = local_offset * Vec3d::UnitX(); break; } @@ -785,10 +764,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: { m_offset = Vec3d::Zero(); break; } } - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from instance coordinates to world coordinates - m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset; - if (selection.is_single_volume_or_modifier()) { if (coordinates_type == ECoordinatesType::Instance) m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset; @@ -849,18 +824,6 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData & data) if (m_hover_id == 6 || m_hover_id == 7) m_offset.y() *= -1.0; - Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection) - - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from world coordinates to instance coordinates - center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset; - - m_offset += (ratio - 1.0) * center_offset; - - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from instance coordinates to world coordinates - m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset; - if (selection.is_single_volume_or_modifier()) { if (coordinates_type == ECoordinatesType::Instance) m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset; @@ -904,7 +867,7 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - const 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; // vector from the starting position to the found intersection const Vec3d inters_vec = inters - m_starting.drag_position; @@ -920,20 +883,5 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const return ratio; } -#if ENABLE_WORLD_COORDINATE -Transform3d GLGizmoScale3D::local_transform(const Selection& selection) const -{ - Transform3d ret = Geometry::translation_transform(m_center); - if (!wxGetApp().obj_manipul()->is_world_coordinates()) { - const GLVolume& v = *selection.get_first_volume(); - Transform3d orient_matrix = v.get_instance_transformation().get_rotation_matrix(); - if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) - orient_matrix = orient_matrix * v.get_volume_transformation().get_rotation_matrix(); - ret = ret * orient_matrix; - } - return ret; -} -#endif // ENABLE_WORLD_COORDINATE - } // namespace GUI } // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 96594786e1..fe6ab4972e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -102,9 +102,6 @@ private: void do_scale_uniform(const UpdateData& data); double calc_ratio(const UpdateData& data) const; -#if ENABLE_WORLD_COORDINATE - Transform3d local_transform(const Selection& selection) const; -#endif // ENABLE_WORLD_COORDINATE }; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 9a56c17436..fd0a5ec595 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -836,108 +836,57 @@ const std::pair& Selection::get_bounding_box_in_curr std::pair Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const { - BoundingBoxf3 original_box; + // + // trafo to current reference system + // Transform3d trafo; - - // - // calculate box aligned to current reference system - // switch (type) { - case ECoordinatesType::World: - { - original_box = get_bounding_box(); - trafo = Transform3d::Identity(); - break; - } - case ECoordinatesType::Instance: { - for (unsigned int id : m_list) { - const GLVolume& v = *get_volume(id); - original_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); - } - trafo = get_first_volume()->get_instance_transformation().get_matrix(); - break; - } - case ECoordinatesType::Local: { - assert(is_single_volume_or_modifier() || is_single_volume_instance()); - const GLVolume& v = *get_first_volume(); - original_box = v.bounding_box(); - trafo = v.world_matrix(); - break; - } + case ECoordinatesType::World: { trafo = Transform3d::Identity(); break; } + case ECoordinatesType::Instance: { trafo = get_first_volume()->get_instance_transformation().get_matrix(); break; } + case ECoordinatesType::Local: { trafo = get_first_volume()->world_matrix(); break; } } // - // calculate box size in world coordinates + // trafo basis in world coordinates // - auto point_to_Vec4d = [](const Vec3d& p) { return Vec4d(p.x(), p.y(), p.z(), 1.0); }; - auto Vec4d_to_Vec3d = [](const Vec4d& v) { return Vec3d(v.x(), v.y(), v.z()); }; + Geometry::Transformation t(trafo); + t.reset_scaling_factor(); + const Transform3d basis_trafo = t.get_matrix_no_offset(); + std::vector axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; + for (size_t i = 0; i < axes.size(); ++i) { + axes[i] = basis_trafo * axes[i]; + } - auto apply_transform = [](const std::vector& original, const Transform3d& trafo, bool normalize) { - std::vector transformed(original.size()); - for (size_t i = 0; i < original.size(); ++i) { - transformed[i] = trafo * original[i]; - if (normalize) - transformed[i].normalize(); - } - return transformed; - }; - - auto calc_box_size = [point_to_Vec4d, Vec4d_to_Vec3d, apply_transform](const BoundingBoxf3& box, const Transform3d& trafo) { - Geometry::Transformation transformation(trafo); - - // box aligned to current reference system - std::vector homo_vertices = { - point_to_Vec4d({ box.min.x(), box.min.y(), box.min.z() }), - point_to_Vec4d({ box.max.x(), box.min.y(), box.min.z() }), - point_to_Vec4d({ box.max.x(), box.max.y(), box.min.z() }), - point_to_Vec4d({ box.min.x(), box.max.y(), box.min.z() }), - point_to_Vec4d({ box.min.x(), box.min.y(), box.max.z() }), - point_to_Vec4d({ box.max.x(), box.min.y(), box.max.z() }), - point_to_Vec4d({ box.max.x(), box.max.y(), box.max.z() }), - point_to_Vec4d({ box.min.x(), box.max.y(), box.max.z() }) - }; - - // box vertices in world coordinates - std::vector transformed_homo_vertices = apply_transform(homo_vertices, trafo, false); - - // project back to current reference system - const std::vector homo_axes = { Vec4d::UnitX(), Vec4d::UnitY(), Vec4d::UnitZ() }; - std::vector transformed_homo_axes = apply_transform(homo_axes, Geometry::Transformation(trafo).get_matrix_no_scaling_factor(), true); - std::vector transformed_axes(transformed_homo_axes.size()); - for (size_t i = 0; i < transformed_homo_axes.size(); ++i) { - transformed_axes[i] = Vec4d_to_Vec3d(transformed_homo_axes[i]); - } - - Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX }; - Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX }; - - for (const Vec4d& v_homo : transformed_homo_vertices) { - const Vec3d v = Vec4d_to_Vec3d(v_homo); + // + // calculate bounding box aligned to trafo basis + // + Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX }; + Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX }; + for (unsigned int id : m_list) { + const GLVolume& vol = *get_volume(id); + const Transform3d vol_world_rafo = vol.world_matrix(); + const TriangleMesh* mesh = vol.convex_hull(); + if (mesh == nullptr) + mesh = &m_model->objects[vol.object_idx()]->volumes[vol.volume_idx()]->mesh(); + assert(mesh != nullptr); + for (const stl_vertex& v : mesh->its.vertices) { + const Vec3d world_v = vol_world_rafo * v.cast(); for (int i = 0; i < 3; ++i) { - const double dot_i = v.dot(transformed_axes[i]); - min(i) = std::min(min(i), dot_i); - max(i) = std::max(max(i), dot_i); + const double i_comp = world_v.dot(axes[i]); + min(i) = std::min(min(i), i_comp); + max(i) = std::max(max(i), i_comp); } } - - // return size - const Vec3d size = max - min; - return size; - }; - - const Vec3d box_size = calc_box_size(original_box, trafo); - const std::vector box_center = { point_to_Vec4d(original_box.center()) }; - std::vector transformed_box_center = apply_transform(box_center, trafo, false); - - // - // return box centered at 0, 0, 0 - // + } + const Vec3d box_size = max - min; const Vec3d half_box_size = 0.5 * box_size; BoundingBoxf3 out_box(-half_box_size, half_box_size); Geometry::Transformation out_trafo(trafo); - out_trafo.set_offset(Vec4d_to_Vec3d(transformed_box_center[0])); + const Vec3d center = 0.5 * (min + max); + out_trafo.set_offset(basis_trafo * center); return { out_box, out_trafo.get_matrix_no_scaling_factor() }; + } #endif // ENABLE_WORLD_COORDINATE @@ -1049,7 +998,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ assert(transformation_type.relative() || (transformation_type.absolute() && transformation_type.local())); - const Transform3d rotation_matrix = Geometry::rotation_transform(rotation); + Transform3d rotation_matrix = Geometry::rotation_transform(rotation); for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; @@ -1074,15 +1023,41 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); } else { - if (transformation_type.local() && transformation_type.absolute()) { + if (transformation_type.instance()) { const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); - Matrix3d vol_rotation, vol_scale; - vol_trafo.get_matrix().computeRotationScaling(&vol_rotation, &vol_scale); - const Transform3d trafo = vol_trafo.get_rotation_matrix() * rotation_matrix; - v.set_volume_transformation(vol_trafo.get_offset_matrix() * trafo * Transform3d(vol_scale)); + const Geometry::Transformation world_trafo = inst_trafo * vol_trafo; + // ensure proper sign of rotation for mirrored objects + if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + rotation_matrix = rotation_matrix.inverse(); + + // ensure that the volume rotates as a rigid body + const Geometry::TransformationSVD world_svd(world_trafo); + if (world_svd.anisotropic_scale) { + const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix(); + rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix; + } + const Transform3d vol_rotation_matrix = vol_trafo.get_rotation_matrix(); + rotation_matrix = vol_rotation_matrix.inverse() * rotation_matrix * vol_rotation_matrix; + + v.set_volume_transformation(vol_trafo.get_matrix() * rotation_matrix); } - else + else { + if (transformation_type.local()) { + const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); + const Geometry::Transformation world_trafo = inst_trafo * vol_trafo; + // ensure proper sign of rotation for mirrored objects + if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + rotation_matrix = rotation_matrix.inverse(); + + // ensure that the volume rotates as a rigid body + const Geometry::TransformationSVD svd(world_trafo); + if (svd.anisotropic_scale) { + const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix(); + rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix; + } + } transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); + } } } } @@ -1091,14 +1066,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ if (m_mode == Instance) { int rot_axis_max = 0; rotation.cwiseAbs().maxCoeff(&rot_axis_max); - SyncRotationType synch; - if (transformation_type.world() && rot_axis_max == 2) - synch = SyncRotationType::NONE; - else if (transformation_type.instance()) - synch = SyncRotationType::FULL; - else - synch = SyncRotationType::GENERAL; - synchronize_unselected_instances(synch); + synchronize_unselected_instances((transformation_type.world() && rot_axis_max == 2) ? SyncRotationType::NONE : SyncRotationType::GENERAL); } else if (m_mode == Volume) synchronize_unselected_volumes(); @@ -1466,31 +1434,29 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation if (!m_valid) return; + Vec3d relative_scale = scale; + if (transformation_type.absolute()) { + // converts to relative scale + if (m_mode == Instance) { + if (is_single_full_instance()) { + BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first; + BoundingBoxf3 original_box; + if (transformation_type.world()) + original_box = get_full_unscaled_instance_bounding_box(); + else + original_box = get_full_unscaled_instance_local_bounding_box(); + + relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size()); + } + } + transformation_type.set_relative(); + } + for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; const VolumeCache& volume_data = m_cache.volumes_data[i]; const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform(); - Vec3d relative_scale = scale; - - if (transformation_type.absolute()) { - if (m_mode == Instance) { - if (is_single_full_instance()) { - BoundingBoxf3 current_box = m_box.get_bounding_box(); - BoundingBoxf3 original_box; - if (transformation_type.world()) - original_box = get_full_unscaled_instance_bounding_box(); - else - original_box = get_full_unscaled_instance_local_bounding_box(); - - relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size()); - transformation_type.set_relative(); - } - } - else { - } - } - if (m_mode == Instance) { if (transformation_type.instance()) { const Vec3d world_inst_pivot = m_cache.dragging_center - inst_trafo.get_offset(); @@ -1529,7 +1495,9 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - synchronize_unselected_instances(SyncRotationType::NONE); + // even if there is no rotation, we pass SyncRotationType::GENERAL to force + // synchronize_unselected_instances() to apply the scale to the other instances + synchronize_unselected_instances(SyncRotationType::GENERAL); else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH @@ -2196,7 +2164,6 @@ void Selection::update_type() 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) { - const ModelVolume* model_volume = model_object->volumes[first->volume_idx()]; m_type = SingleFullObject; // ensures the correct mode is selected m_mode = Instance; @@ -2834,7 +2801,7 @@ void Selection::render_debug_window() const } static int current_method_idx = 0; - ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0"); + ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0SVD\0"); const GLVolume& v = *get_volume(current_vol_idx); @@ -2854,23 +2821,56 @@ void Selection::render_debug_window() const ImGui::EndGroup(); }; - auto add_matrices_set = [add_matrix](const std::string& name, const Transform3d& m, size_t method) { + auto add_matrices_set = [&imgui, add_matrix](const std::string& name, const Transform3d& m, size_t method) { static unsigned int counter = 0; ++counter; if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { add_matrix("Full", m, 4); - Matrix3d rotation; - Matrix3d scale; - if (method == 0) - m.computeRotationScaling(&rotation, &scale); - else - m.computeScalingRotation(&scale, &rotation); + if (method == 0 || method == 1) { + Matrix3d rotation; + Matrix3d scale; + if (method == 0) + m.computeRotationScaling(&rotation, &scale); + else + m.computeScalingRotation(&scale, &rotation); - ImGui::SameLine(); - add_matrix("Rotation component", Transform3d(rotation), 3); - ImGui::SameLine(); - add_matrix("Scale component", Transform3d(scale), 3); + ImGui::SameLine(); + add_matrix("Rotation component", Transform3d(rotation), 3); + ImGui::SameLine(); + add_matrix("Scale component", Transform3d(scale), 3); + } + else { + const Geometry::TransformationSVD svd(m); + + ImGui::SameLine(); + add_matrix("U", Transform3d(svd.u), 3); + ImGui::SameLine(); + add_matrix("S", Transform3d(svd.s), 3); + ImGui::SameLine(); + add_matrix("V", Transform3d(svd.v), 3); + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + float spacing = 0.0f; + if (svd.rotation) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.rotation_90_degrees ? "Rotation 90 degs" : "Rotation"); + spacing = 10.0f; + } + if (svd.scale) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.anisotropic_scale ? "Anisotropic scale" : "Isotropic scale"); + spacing = 10.0f; + } + if (svd.mirror) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Mirror"); + spacing = 10.0f; + } + if (svd.skew) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Skew"); + } + } } }; @@ -2968,10 +2968,10 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV assert(idx_volume_first != -1); // object without instances? if (idx_volume_first == -1) continue; - const Transform3d::ConstLinearPart &rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); + const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i) if (volumes[i]->object_idx() == idx_object) { - const Transform3d::ConstLinearPart &rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); + const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); assert(is_rotation_xy_synchronized(rotation, rotation0)); } } @@ -2994,7 +2994,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ const int object_idx = volume_i->object_idx(); const int instance_idx = volume_i->instance_idx(); const Transform3d& curr_inst_trafo_i = volume_i->get_instance_transformation().get_matrix(); - const bool curr_inst_left_handed = is_left_handed(curr_inst_trafo_i); const Transform3d& old_inst_trafo_i = m_cache.volumes_data[i].get_instance_transform().get_matrix(); bool mirrored = is_left_handed(curr_inst_trafo_i) != is_left_handed(old_inst_trafo_i); // bool mirrored = curr_inst_trafo_i.linear().determinant() * old_inst_trafo_i.linear().determinant() < 0; diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8a2ba1bfa1..c1c97bd2e6 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -499,10 +499,6 @@ public: NONE = 0, // Synchronize after rotation by an axis not parallel with Z. 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();