Merge remote-tracking branch 'origin/et_transformations'
This commit is contained in:
commit
fc6ed2eb6e
14 changed files with 344 additions and 336 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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", ¤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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue