Merge remote-tracking branch 'remotes/origin/objects_centering'
This commit is contained in:
commit
3e0ef05386
@ -1188,16 +1188,16 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
|
||||
Vec3d angles2 = Vec3d::Zero();
|
||||
if (is_approx(std::abs(rotation_matrix(2, 0)), 1.0))
|
||||
{
|
||||
angles1(0) = 0.0;
|
||||
if (rotation_matrix(2, 0) > 0.0) // == +1.0
|
||||
angles1(2) = 0.0;
|
||||
if (rotation_matrix(2, 0) < 0.0) // == -1.0
|
||||
{
|
||||
angles1(1) = 0.5 * (double)PI;
|
||||
angles1(2) = angles1(0) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||
}
|
||||
else // == -1.0
|
||||
else // == 1.0
|
||||
{
|
||||
angles1(1) = 0.5 * (double)PI;
|
||||
angles1(2) = -angles1(0) - ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||
angles1(1) = - 0.5 * (double)PI;
|
||||
angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
|
||||
}
|
||||
angles2 = angles1;
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ public:
|
||||
void set_rotation(const Vec3d& rotation);
|
||||
void set_rotation(Axis axis, double rotation);
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_scaling_factor; }
|
||||
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
|
||||
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||
|
@ -473,7 +473,7 @@ public:
|
||||
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
||||
|
@ -1770,7 +1770,7 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local)
|
||||
|
||||
#if !DISABLE_INSTANCES_SYNCH
|
||||
if (m_mode == Instance)
|
||||
_synchronize_unselected_instances();
|
||||
_synchronize_unselected_instances(SYNC_ROTATION_NONE);
|
||||
else if (m_mode == Volume)
|
||||
_synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
@ -1778,64 +1778,100 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local)
|
||||
m_bounding_box_dirty = true;
|
||||
}
|
||||
|
||||
static Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||
{
|
||||
return
|
||||
// From the current coordinate system to world.
|
||||
Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) *
|
||||
// From world to the initial coordinate system.
|
||||
Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ());
|
||||
}
|
||||
|
||||
// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis.
|
||||
static double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||
{
|
||||
Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
|
||||
Vec3d axis = angle_axis.axis();
|
||||
double angle = angle_axis.angle();
|
||||
#ifdef _DEBUG
|
||||
if (std::abs(angle) > 1e-8) {
|
||||
assert(std::abs(axis.x()) < 1e-8);
|
||||
assert(std::abs(axis.y()) < 1e-8);
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
return (axis.z() < 0) ? -angle : angle;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
if (is_single_full_instance())
|
||||
{
|
||||
if (local)
|
||||
(*m_volumes)[i]->set_instance_rotation(rotation);
|
||||
else
|
||||
{
|
||||
int rot_axis_max;
|
||||
rotation.cwiseAbs().maxCoeff(&rot_axis_max);
|
||||
|
||||
// For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it.
|
||||
std::vector<int> object_instance_first(m_model->objects.size(), -1);
|
||||
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, local](GLVolume &volume, int i) {
|
||||
int first_volume_idx = object_instance_first[volume.object_idx()];
|
||||
if (rot_axis_max != 2 && first_volume_idx != -1) {
|
||||
// Generic rotation, but no rotation around the Z axis.
|
||||
// Always do a local rotation (do not consider the selection to be a rigid body).
|
||||
assert(rotation.z() == 0);
|
||||
const GLVolume &first_volume = *(*m_volumes)[first_volume_idx];
|
||||
const Vec3d &rotation = first_volume.get_instance_rotation();
|
||||
double z_diff = rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation());
|
||||
volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
||||
} else {
|
||||
// extracts rotations from the composed transformation
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
||||
(*m_volumes)[i]->set_instance_rotation(new_rotation);
|
||||
}
|
||||
if (rot_axis_max == 2 && !local)
|
||||
// Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis.
|
||||
volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
volume.set_instance_rotation(new_rotation);
|
||||
object_instance_first[volume.object_idx()] = i;
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
GLVolume &volume = *(*m_volumes)[i];
|
||||
if (is_single_full_instance())
|
||||
rotate_instance(volume, i);
|
||||
else if (is_single_volume() || is_single_modifier())
|
||||
{
|
||||
if (local)
|
||||
(*m_volumes)[i]->set_volume_rotation(rotation);
|
||||
volume.set_volume_rotation(rotation);
|
||||
else
|
||||
{
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
||||
volume.set_volume_rotation(new_rotation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
if (m_mode == Instance)
|
||||
{
|
||||
// extracts rotations from the composed transformation
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
||||
if (!local)
|
||||
(*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||
|
||||
(*m_volumes)[i]->set_instance_rotation(new_rotation);
|
||||
}
|
||||
rotate_instance(volume, i);
|
||||
else if (m_mode == Volume)
|
||||
{
|
||||
// extracts rotations from the composed transformation
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||
if (!local)
|
||||
{
|
||||
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
|
||||
(*m_volumes)[i]->set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
|
||||
volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
|
||||
}
|
||||
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
||||
volume.set_volume_rotation(new_rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !DISABLE_INSTANCES_SYNCH
|
||||
if (m_mode == Instance)
|
||||
_synchronize_unselected_instances();
|
||||
_synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL);
|
||||
else if (m_mode == Volume)
|
||||
_synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
@ -1878,7 +1914,7 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal)
|
||||
// we want to synchronize z-rotation as well, otherwise the flattening behaves funny
|
||||
// when applied on one of several identical instances
|
||||
if (m_mode == Instance)
|
||||
_synchronize_unselected_instances(true);
|
||||
_synchronize_unselected_instances(SYNC_ROTATION_FULL);
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
|
||||
m_bounding_box_dirty = true;
|
||||
@ -1925,7 +1961,7 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local)
|
||||
|
||||
#if !DISABLE_INSTANCES_SYNCH
|
||||
if (m_mode == Instance)
|
||||
_synchronize_unselected_instances();
|
||||
_synchronize_unselected_instances(SYNC_ROTATION_NONE);
|
||||
else if (m_mode == Volume)
|
||||
_synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
@ -1952,7 +1988,7 @@ void GLCanvas3D::Selection::mirror(Axis axis)
|
||||
|
||||
#if !DISABLE_INSTANCES_SYNCH
|
||||
if (m_mode == Instance)
|
||||
_synchronize_unselected_instances();
|
||||
_synchronize_unselected_instances(SYNC_ROTATION_NONE);
|
||||
else if (m_mode == Volume)
|
||||
_synchronize_unselected_volumes();
|
||||
#endif // !DISABLE_INSTANCES_SYNCH
|
||||
@ -2750,7 +2786,43 @@ void GLCanvas3D::Selection::_render_sidebar_size_hint(Axis axis, double length)
|
||||
{
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::_synchronize_unselected_instances(bool including_z)
|
||||
#ifdef _DEBUG
|
||||
static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||
{
|
||||
Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
|
||||
Vec3d axis = angle_axis.axis();
|
||||
double angle = angle_axis.angle();
|
||||
if (std::abs(angle) < 1e-8)
|
||||
return true;
|
||||
assert(std::abs(axis.x()) < 1e-8);
|
||||
assert(std::abs(axis.y()) < 1e-8);
|
||||
assert(std::abs(std::abs(axis.z()) - 1.) < 1e-8);
|
||||
return std::abs(axis.x()) < 1e-8 && std::abs(axis.y()) < 1e-8 && std::abs(std::abs(axis.z()) - 1.) < 1e-8;
|
||||
}
|
||||
static void verify_instances_rotation_synchronized(const Model &model, const GLVolumePtrs &volumes)
|
||||
{
|
||||
for (size_t idx_object = 0; idx_object < model.objects.size(); ++ idx_object) {
|
||||
int idx_volume_first = -1;
|
||||
for (int i = 0; i < (int)volumes.size(); ++ i) {
|
||||
if (volumes[i]->object_idx() == idx_object) {
|
||||
idx_volume_first = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(idx_volume_first != -1); // object without instances?
|
||||
if (idx_volume_first == -1)
|
||||
continue;
|
||||
const Vec3d &rotation0 = volumes[idx_volume_first]->get_instance_rotation();
|
||||
for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++ i)
|
||||
if (volumes[i]->object_idx() == idx_object) {
|
||||
const Vec3d &rotation = volumes[i]->get_instance_rotation();
|
||||
assert(is_rotation_xy_synchronized(rotation, rotation0));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
|
||||
void GLCanvas3D::Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation_type)
|
||||
{
|
||||
std::set<unsigned int> done; // prevent processing volumes twice
|
||||
done.insert(m_list.begin(), m_list.end());
|
||||
@ -2783,26 +2855,34 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances(bool including_z)
|
||||
if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx))
|
||||
continue;
|
||||
|
||||
auto is_approx = [](double value, double test_value) -> bool { return std::abs(value - test_value) < EPSILON; };
|
||||
|
||||
double z;
|
||||
if (including_z)
|
||||
// rotation comes from place on face -> force given z
|
||||
z = rotation(2);
|
||||
else if (is_approx(rotation(0), m_cache.volumes_data[j].get_instance_rotation()(0)) && is_approx(rotation(1), m_cache.volumes_data[j].get_instance_rotation()(1)))
|
||||
assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()));
|
||||
switch (sync_rotation_type) {
|
||||
case SYNC_ROTATION_NONE:
|
||||
// z only rotation -> keep instance z
|
||||
z = v->get_instance_rotation()(2);
|
||||
else
|
||||
// generic rotation -> update instance z
|
||||
z = m_cache.volumes_data[j].get_instance_rotation()(2) + rotation(2);
|
||||
// The X,Y rotations should be synchronized from start to end of the rotation.
|
||||
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
|
||||
break;
|
||||
case SYNC_ROTATION_FULL:
|
||||
// rotation comes from place on face -> force given z
|
||||
v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2)));
|
||||
break;
|
||||
case SYNC_ROTATION_GENERAL:
|
||||
// generic rotation -> update instance z with the delta of the rotation.
|
||||
double z_diff = rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation());
|
||||
v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
||||
break;
|
||||
}
|
||||
|
||||
v->set_instance_rotation(Vec3d(rotation(0), rotation(1), z));
|
||||
v->set_instance_scaling_factor(scaling_factor);
|
||||
v->set_instance_mirror(mirror);
|
||||
|
||||
done.insert(j);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
verify_instances_rotation_synchronized(*m_model, *m_volumes);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::_synchronize_unselected_volumes()
|
||||
|
@ -670,7 +670,15 @@ public:
|
||||
void _render_sidebar_rotation_hint(Axis axis) const;
|
||||
void _render_sidebar_scale_hint(Axis axis) const;
|
||||
void _render_sidebar_size_hint(Axis axis, double length) const;
|
||||
void _synchronize_unselected_instances(bool including_z = false);
|
||||
enum SyncRotationType {
|
||||
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||
SYNC_ROTATION_NONE = 0,
|
||||
// Synchronize fully. Used from "place on bed" feature.
|
||||
SYNC_ROTATION_FULL = 1,
|
||||
// Synchronize after rotation by an axis not parallel with Z.
|
||||
SYNC_ROTATION_GENERAL = 2,
|
||||
};
|
||||
void _synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void _synchronize_unselected_volumes();
|
||||
void _ensure_on_bed();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user