1) Reworked logic for pasting volumes

2) Fixed paste of volumes into different objects

3) Do not apply offset when pasting into the copied object

4) Keep source transformation matrix and relative positions when copy/pasting volumes into another object
This commit is contained in:
Enrico Turri 2019-06-24 12:26:11 +02:00
parent 1c895ee6a0
commit 6b0d75127b
4 changed files with 103 additions and 67 deletions

View File

@ -1409,6 +1409,63 @@ Transformation Transformation::operator * (const Transformation& other) const
return Transformation(get_matrix() * other.get_matrix());
}
Transformation Transformation::volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox)
{
Transformation out;
if (instance_transformation.is_scaling_uniform()) {
// No need to run the non-linear least squares fitting for uniform scaling.
// Just set the inverse.
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
}
else if (is_rotation_ninety_degrees(instance_transformation.get_rotation()))
{
// Anisotropic scaling, rotation by multiples of ninety degrees.
Eigen::Matrix3d instance_rotation_trafo =
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().x(), Vec3d::UnitX())).toRotationMatrix();
Eigen::Matrix3d volume_rotation_trafo =
(Eigen::AngleAxisd(-instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
// 8 corners of the bounding box.
auto pts = Eigen::MatrixXd(8, 3);
pts(0, 0) = bbox.min.x(); pts(0, 1) = bbox.min.y(); pts(0, 2) = bbox.min.z();
pts(1, 0) = bbox.min.x(); pts(1, 1) = bbox.min.y(); pts(1, 2) = bbox.max.z();
pts(2, 0) = bbox.min.x(); pts(2, 1) = bbox.max.y(); pts(2, 2) = bbox.min.z();
pts(3, 0) = bbox.min.x(); pts(3, 1) = bbox.max.y(); pts(3, 2) = bbox.max.z();
pts(4, 0) = bbox.max.x(); pts(4, 1) = bbox.min.y(); pts(4, 2) = bbox.min.z();
pts(5, 0) = bbox.max.x(); pts(5, 1) = bbox.min.y(); pts(5, 2) = bbox.max.z();
pts(6, 0) = bbox.max.x(); pts(6, 1) = bbox.max.y(); pts(6, 2) = bbox.min.z();
pts(7, 0) = bbox.max.x(); pts(7, 1) = bbox.max.y(); pts(7, 2) = bbox.max.z();
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
auto qs = pts *
(instance_rotation_trafo *
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror())) *
volume_rotation_trafo).inverse().transpose();
// Fill in scaling based on least squares fitting of the bounding box corners.
Vec3d scale;
for (int i = 0; i < 3; ++i)
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
}
else
{
// General anisotropic scaling, general rotation.
// Keep the modifier mesh in the instance coordinate system, so the modifier mesh will not be aligned with the world.
// Scale it to get the required size.
out.set_scaling_factor(instance_transformation.get_scaling_factor().cwiseInverse());
}
return out;
}
Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
{
return

View File

@ -258,6 +258,11 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
Transformation operator * (const Transformation& other) const;
// Find volume transformation, so that the chained (instance_trafo * volume_trafo) will be as close to identity
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox);
};
// Rotation when going from the first coordinate system with rotation rot_xyz_from applied

View File

@ -1485,66 +1485,6 @@ void ObjectList::load_part( ModelObject* model_object,
}
// Find volume transformation, so that the chained (instance_trafo * volume_trafo) will be as close to identity
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
Geometry::Transformation volume_to_bed_transformation(const Geometry::Transformation &instance_transformation, const BoundingBoxf3 &bbox)
{
Geometry::Transformation out;
if (instance_transformation.is_scaling_uniform()) {
// No need to run the non-linear least squares fitting for uniform scaling.
// Just set the inverse.
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
}
else if (Geometry::is_rotation_ninety_degrees(instance_transformation.get_rotation()))
{
// Anisotropic scaling, rotation by multiples of ninety degrees.
Eigen::Matrix3d instance_rotation_trafo =
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().x(), Vec3d::UnitX())).toRotationMatrix();
Eigen::Matrix3d volume_rotation_trafo =
(Eigen::AngleAxisd(-instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
// 8 corners of the bounding box.
auto pts = Eigen::MatrixXd(8, 3);
pts(0, 0) = bbox.min.x(); pts(0, 1) = bbox.min.y(); pts(0, 2) = bbox.min.z();
pts(1, 0) = bbox.min.x(); pts(1, 1) = bbox.min.y(); pts(1, 2) = bbox.max.z();
pts(2, 0) = bbox.min.x(); pts(2, 1) = bbox.max.y(); pts(2, 2) = bbox.min.z();
pts(3, 0) = bbox.min.x(); pts(3, 1) = bbox.max.y(); pts(3, 2) = bbox.max.z();
pts(4, 0) = bbox.max.x(); pts(4, 1) = bbox.min.y(); pts(4, 2) = bbox.min.z();
pts(5, 0) = bbox.max.x(); pts(5, 1) = bbox.min.y(); pts(5, 2) = bbox.max.z();
pts(6, 0) = bbox.max.x(); pts(6, 1) = bbox.max.y(); pts(6, 2) = bbox.min.z();
pts(7, 0) = bbox.max.x(); pts(7, 1) = bbox.max.y(); pts(7, 2) = bbox.max.z();
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
auto qs = pts *
(instance_rotation_trafo *
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror())) *
volume_rotation_trafo).inverse().transpose();
// Fill in scaling based on least squares fitting of the bounding box corners.
Vec3d scale;
for (int i = 0; i < 3; ++ i)
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
}
else
{
// General anisotropic scaling, general rotation.
// Keep the modifier mesh in the instance coordinate system, so the modifier mesh will not be aligned with the world.
// Scale it to get the required size.
out.set_scaling_factor(instance_transformation.get_scaling_factor().cwiseInverse());
}
return out;
}
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
{
const auto obj_idx = get_selected_obj_idx();
@ -1598,7 +1538,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
// Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
// Set the modifier position.
auto offset = (type_name == "Slab") ?
// Slab: Lift to print bed

View File

@ -1893,25 +1893,59 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
void Selection::paste_volumes_from_clipboard()
{
int obj_idx = get_object_idx();
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
int dst_obj_idx = get_object_idx();
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
return;
ModelObject* dst_object = m_model->objects[dst_obj_idx];
int dst_inst_idx = get_instance_idx();
if ((dst_inst_idx < 0) || ((int)dst_object->instances.size() <= dst_inst_idx))
return;
ModelObject* src_object = m_clipboard.get_object(0);
if (src_object != nullptr)
{
ModelObject* dst_object = m_model->objects[obj_idx];
ModelInstance* dst_instance = dst_object->instances[dst_inst_idx];
BoundingBoxf3 dst_instance_bb = dst_object->instance_bounding_box(dst_inst_idx);
Transform3d src_matrix = src_object->instances[0]->get_transformation().get_matrix(true);
Transform3d dst_matrix = dst_instance->get_transformation().get_matrix(true);
bool from_same_object = (src_object->input_file == dst_object->input_file) && src_matrix.isApprox(dst_matrix);
// used to keep relative position of multivolume selections when pasting from another object
BoundingBoxf3 total_bb;
ModelVolumePtrs volumes;
for (ModelVolume* src_volume : src_object->volumes)
{
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
dst_volume->set_new_unique_id();
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
dst_volume->translate(offset, offset, 0.0);
if (from_same_object)
{
// // if the volume comes from the same object, apply the offset in world system
// double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
// dst_volume->translate(dst_matrix.inverse() * Vec3d(offset, offset, 0.0));
}
else
{
// if the volume comes from another object, apply the offset as done when adding modifiers
// see ObjectList::load_generic_subobject()
total_bb.merge(dst_volume->mesh().bounding_box().transformed(src_volume->get_matrix()));
}
volumes.push_back(dst_volume);
}
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
// keeps relative position of multivolume selections
if (!from_same_object)
{
for (ModelVolume* v : volumes)
{
v->set_offset((v->get_offset() - total_bb.center()) + dst_matrix.inverse() * (Vec3d(dst_instance_bb.max(0), dst_instance_bb.min(1), dst_instance_bb.min(2)) + 0.5 * total_bb.size() - dst_instance->get_transformation().get_offset()));
}
}
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
}
}