From 1130d78c19ffca26ea21632a98640be4f7053a6d Mon Sep 17 00:00:00 2001 From: Lukas Matena <lukasmatena@seznam.cz> Date: Thu, 29 Oct 2020 12:21:32 +0100 Subject: [PATCH] Fix of #3505 (place on face oriented objects at slight angle sometimes) --- src/slic3r/GUI/Selection.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 5547589b9..f7a62a468 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -853,6 +853,7 @@ void Selection::flattening_rotate(const Vec3d& normal) // We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out // how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances. // The function assumes that is_from_single_object() holds. + assert(Slic3r::is_approx(normal.norm(), 1.)); if (!m_valid) return; @@ -866,14 +867,31 @@ void Selection::flattening_rotate(const Vec3d& normal) Vec3d mirror(wmt(0, 0), wmt(1, 1), wmt(2, 2)); Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix()); - Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor, mirror) * normal; - transformed_normal.normalize(); + Vec3d tnormal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor, mirror) * normal; + tnormal.normalize(); - Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + // Calculate rotation axis. It shall be perpendicular to "down" direction + // and the normal, so the rotation is the shortest possible and logical. + Vec3d axis = tnormal.cross(-Vec3d::UnitZ()); + + // Make sure the axis is not zero and normalize it. "Almost" zero is not interesting. + // In case the vectors are almost colinear, the rotation axis does not matter much. + if (axis == Vec3d::Zero()) + axis = Vec3d::UnitX(); axis.normalize(); + // Calculate the angle using the component where we achieve more precision. + // Cosine of small angles is const in first order. No good. + double angle = 0.; + if (std::abs(tnormal.z()) < std::sqrt(2.)/2.) + angle = std::acos(-tnormal.z()); + else { + double xy = std::hypot(tnormal.x(), tnormal.y()); + angle = PI/2. + std::acos(xy * (tnormal.z() > 0.)); + } + Transform3d extra_rotation = Transform3d::Identity(); - extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + extra_rotation.rotate(Eigen::AngleAxisd(angle, axis)); Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix()); (*m_volumes)[i]->set_instance_rotation(new_rotation);