Merge remote-tracking branch 'origin/et_transformations'

This commit is contained in:
enricoturri1966 2023-02-16 12:46:45 +01:00
commit fc6ed2eb6e
14 changed files with 344 additions and 336 deletions

View file

@ -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<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> 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)
{

View file

@ -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<double, 3, 3> mirror_matrix() const { return Eigen::DiagonalMatrix<double, 3, 3>(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);

View file

@ -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<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
double mitter_limit = scale_(0.1);
const float shrink_factor = static_cast<float>(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());

View file

@ -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<GLVolume*>(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<GLVolume*>(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<GLVolume*>(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero());
const_cast<GLVolume*>(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<GLVolume*>(selection.get_first_volume())->set_volume_scaling_factor(Vec3d::Ones());
if (is_left_handed)
const_cast<GLVolume*>(selection.get_first_volume())->set_volume_mirror({ -1.0 , 1.0, 1.0 });
GLVolume* vol = const_cast<GLVolume*>(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<GLVolume*>(selection.get_volume(idx))->set_instance_scaling_factor(Vec3d::Ones());
if (is_left_handed)
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_mirror({ -1.0 , 1.0, 1.0 });
const_cast<GLVolume*>(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<MirrorButtonState, 3> 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.

View file

@ -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();
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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<int, 4>& 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<int, 4>& 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<int, 4>& 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<int, 4>& 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<int, 4>& 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

View file

@ -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
};

View file

@ -836,108 +836,57 @@ const std::pair<BoundingBoxf3, Transform3d>& Selection::get_bounding_box_in_curr
std::pair<BoundingBoxf3, Transform3d> 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<Vec3d> 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<Vec4d>& original, const Transform3d& trafo, bool normalize) {
std::vector<Vec4d> 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<Vec4d> 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<Vec4d> transformed_homo_vertices = apply_transform(homo_vertices, trafo, false);
// project back to current reference system
const std::vector<Vec4d> homo_axes = { Vec4d::UnitX(), Vec4d::UnitY(), Vec4d::UnitZ() };
std::vector<Vec4d> transformed_homo_axes = apply_transform(homo_axes, Geometry::Transformation(trafo).get_matrix_no_scaling_factor(), true);
std::vector<Vec3d> 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<double>();
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<Vec4d> box_center = { point_to_Vec4d(original_box.center()) };
std::vector<Vec4d> 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", &current_method_idx, "computeRotationScaling\0computeScalingRotation\0");
ImGui::Combo("Decomposition method", &current_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;

View file

@ -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();