diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 031e489f70..b043b1787e 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -313,7 +313,6 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, return transform; } -#if ENABLE_TRANSFORMATIONS_BY_MATRICES void assemble_transform(Transform3d& transform, const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror) { transform = translation * rotation * scale * mirror; @@ -326,6 +325,19 @@ Transform3d assemble_transform(const Transform3d& translation, const Transform3d return transform; } +void translation_transform(Transform3d& transform, const Vec3d& translation) +{ + transform = Transform3d::Identity(); + transform.translate(translation); +} + +Transform3d translation_transform(const Vec3d& translation) +{ + Transform3d transform; + translation_transform(transform, translation); + return transform; +} + void rotation_transform(Transform3d& transform, const Vec3d& rotation) { transform = Transform3d::Identity(); @@ -351,7 +363,6 @@ Transform3d scale_transform(const Vec3d& scale) scale_transform(transform, scale); return transform; } -#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES Vec3d extract_euler_angles(const Eigen::Matrix& rotation_matrix) { @@ -433,6 +444,14 @@ static std::pair extract_rotation_scale(const Transfor return { Transform3d(rotation), Transform3d(scale) }; } +static bool contains_skew(const Transform3d& trafo) +{ + Matrix3d rotation; + Matrix3d scale; + trafo.computeRotationScaling(&rotation, &scale); + return !scale.isDiagonal(); +} + Vec3d Transformation::get_rotation() const { return extract_euler_angles(extract_rotation(m_matrix)); @@ -623,6 +642,12 @@ void Transformation::set_mirror(Axis axis, double mirror) #endif // ENABLE_TRANSFORMATIONS_BY_MATRICES } +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +bool Transformation::has_skew() const +{ + return contains_skew(m_matrix); +} + #if !ENABLE_TRANSFORMATIONS_BY_MATRICES void Transformation::set_from_transform(const Transform3d& transform) { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index bd8326bb65..0f0380490f 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -336,7 +336,6 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones()); -#if ENABLE_TRANSFORMATIONS_BY_MATRICES // Sets the given transform by multiplying the given transformations in the following order: // T = translation * rotation * scale * mirror void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d::Identity(), @@ -348,6 +347,12 @@ void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d assemble_transform(const Transform3d& translation = Transform3d::Identity(), const Transform3d& rotation = Transform3d::Identity(), const Transform3d& scale = Transform3d::Identity(), const Transform3d& mirror = Transform3d::Identity()); +// Sets the given transform by assembling the given translation +void translation_transform(Transform3d& transform, const Vec3d& translation); + +// Returns the transform obtained by assembling the given translation +Transform3d translation_transform(const Vec3d& translation); + // Sets the given transform by assembling the given rotations in the following order: // 1) rotate X // 2) rotate Y @@ -365,7 +370,6 @@ void scale_transform(Transform3d& transform, const Vec3d& scale); // Returns the transform obtained by assembling the given scale factors Transform3d scale_transform(const Vec3d& scale); -#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES // Returns the euler angles extracted from the given rotation matrix // Warning -> The matrix should not contain any scale or shear !!! @@ -478,6 +482,10 @@ public: void set_mirror(const Vec3d& mirror); void set_mirror(Axis axis, double mirror); +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + bool has_skew() const; +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES + #if !ENABLE_TRANSFORMATIONS_BY_MATRICES void set_from_transform(const Transform3d& transform); #endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9e73aaf8b3..0fd6997276 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2914,7 +2914,13 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) else displacement = multiplier * direction; +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + TransformationType trafo_type; + trafo_type.set_relative(); + m_selection.translate(displacement, trafo_type); +#else m_selection.translate(displacement); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES m_dirty = true; } ); @@ -3579,7 +3585,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + TransformationType trafo_type; + trafo_type.set_relative(); + m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type); +#else m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) update_sequential_clearance(); wxGetApp().obj_manipul()->set_dirty(); @@ -3996,12 +4008,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) Selection::EMode selection_mode = m_selection.get_mode(); for (const GLVolume* v : m_volumes.volumes) { - int object_idx = v->object_idx(); + const int object_idx = v->object_idx(); if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; - int instance_idx = v->instance_idx(); - int volume_idx = v->volume_idx(); + const int instance_idx = v->instance_idx(); + const int volume_idx = v->volume_idx(); done.insert(std::pair(object_idx, instance_idx)); @@ -4009,13 +4021,22 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { if (selection_mode == Selection::Instance) { +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); +#else model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES } else if (selection_mode == Selection::Volume) { +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); + model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); +#else model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES } model_object->invalidate_bounding_box(); } @@ -4024,10 +4045,10 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) // Fixes sinking/flying instances for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; - double shift_z = m->get_instance_min_z(i.second); + const double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) { - Vec3d shift(0.0, 0.0, -shift_z); + const Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 9b892eb53f..871c90c51f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -461,9 +461,16 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_reset_scale_button->SetToolTip(_L("Reset scale")); m_reset_scale_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale")); +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + bool old_uniform = m_uniform_scale; + m_uniform_scale = true; + change_scale_value(0, 100.0); + m_uniform_scale = old_uniform; +#else change_scale_value(0, 100.); change_scale_value(1, 100.); change_scale_value(2, 100.); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES }); editors_grid_sizer->Add(m_reset_scale_button); @@ -616,6 +623,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_position = volume->get_instance_offset(); #endif // !ENABLE_WORLD_COORDINATE +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. #if ENABLE_WORLD_COORDINATE if (is_world_coordinates() && !m_uniform_scale && @@ -627,6 +635,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_uniform_scale = true; m_lock_bnt->SetLock(true); } +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_WORLD_COORDINATE if (is_world_coordinates()) { @@ -790,6 +799,7 @@ void ObjectManipulation::update_if_dirty() update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); } +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_WORLD_COORDINATE Selection::EUniformScaleRequiredReason reason; if (selection.requires_uniform_scale(&reason)) { @@ -829,10 +839,13 @@ void ObjectManipulation::update_if_dirty() #endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED } else { +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES m_lock_bnt->SetLock(m_uniform_scale); m_lock_bnt->SetToolTip(wxEmptyString); m_lock_bnt->enable(); +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES } +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES #if !ENABLE_WORLD_COORDINATE { @@ -1048,7 +1061,19 @@ void ObjectManipulation::change_position_value(int axis, double value) Selection& selection = canvas->get_selection(); selection.setup_cache(); #if ENABLE_WORLD_COORDINATE +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + TransformationType trafo_type; + trafo_type.set_relative(); + switch (get_coordinates_type()) + { + case ECoordinatesType::Instance: { trafo_type.set_instance(); break; } + case ECoordinatesType::Local: { trafo_type.set_local(); break; } + default: { break; } + } + selection.translate(position - m_cache.position, trafo_type); +#else selection.translate(position - m_cache.position, get_coordinates_type()); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else selection.translate(position - m_cache.position, selection.requires_local_axes()); #endif // ENABLE_WORLD_COORDINATE @@ -1191,6 +1216,9 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const transformation_type.set_local(); #endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + Vec3d scaling_factor = m_uniform_scale ? scale(axis) * Vec3d::Ones() : scale; +#else bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale(); Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale; @@ -1203,6 +1231,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const scaling_factor = (mv * mi * scaling_factor).cwiseAbs(); } } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else TransformationType transformation_type(TransformationType::World_Relative_Joint); if (selection.is_single_full_instance()) { @@ -1231,6 +1260,12 @@ void ObjectManipulation::do_size(int axis, const Vec3d& scale) const else if (is_instance_coordinates()) transformation_type.set_instance(); + if (!is_local_coordinates()) + transformation_type.set_relative(); + +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + Vec3d scaling_factor = m_uniform_scale ? scale(axis) * Vec3d::Ones() : scale; +#else bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale(); Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale; @@ -1243,6 +1278,7 @@ void ObjectManipulation::do_size(int axis, const Vec3d& scale) const scaling_factor = (mv * mi * scaling_factor).cwiseAbs(); } } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES selection.setup_cache(); selection.scale(scaling_factor, transformation_type); @@ -1303,6 +1339,10 @@ void ObjectManipulation::set_uniform_scaling(const bool use_uniform_scale) } m_uniform_scale = use_uniform_scale; + +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + set_dirty(); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else #if ENABLE_WORLD_COORDINATE if (selection.is_single_full_instance() && is_world_coordinates() && !use_uniform_scale) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index e2e3223de2..bee57ec6ec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -1,23 +1,23 @@ -#include "GLGizmoBase.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" - -#include - -#include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/GUI_ObjectManipulation.hpp" -#if ENABLE_GL_SHADERS_ATTRIBUTES -#include "slic3r/GUI/Plater.hpp" -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - -// TODO: Display tooltips quicker on Linux - -namespace Slic3r { -namespace GUI { - -const float GLGizmoBase::Grabber::SizeFactor = 0.05f; -const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; -const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; - +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#if ENABLE_GL_SHADERS_ATTRIBUTES +#include "slic3r/GUI/Plater.hpp" +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + +// TODO: Display tooltips quicker on Linux + +namespace Slic3r { +namespace GUI { + +const float GLGizmoBase::Grabber::SizeFactor = 0.05f; +const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; +const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; + #if ENABLE_GIZMO_GRABBER_REFACTOR GLModel GLGizmoBase::Grabber::s_cube; GLModel GLGizmoBase::Grabber::s_cone; @@ -30,35 +30,35 @@ GLGizmoBase::Grabber::~Grabber() if (s_cone.is_initialized()) s_cone.reset(); } -#endif // ENABLE_GIZMO_GRABBER_REFACTOR - -float GLGizmoBase::Grabber::get_half_size(float size) const -{ - return std::max(size * SizeFactor, MinHalfSize); -} - -float GLGizmoBase::Grabber::get_dragging_half_size(float size) const -{ - return get_half_size(size) * DraggingScaleFactor; -} - -void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, bool picking) -{ -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = wxGetApp().get_current_shader(); - if (shader == nullptr) - return; -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + +float GLGizmoBase::Grabber::get_half_size(float size) const +{ + return std::max(size * SizeFactor, MinHalfSize); +} + +float GLGizmoBase::Grabber::get_dragging_half_size(float size) const +{ + return get_half_size(size) * DraggingScaleFactor; +} + +void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, bool picking) +{ +#if ENABLE_GL_SHADERS_ATTRIBUTES + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr) + return; +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + #if ENABLE_GIZMO_GRABBER_REFACTOR if (!s_cube.is_initialized()) { #else if (!m_cube.is_initialized()) { -#endif // ENABLE_GIZMO_GRABBER_REFACTOR - // This cannot be done in constructor, OpenGL is not yet - // initialized at that point (on Linux at least). - indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); - its_translate(its, -0.5f * Vec3f::Ones()); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + // This cannot be done in constructor, OpenGL is not yet + // initialized at that point (on Linux at least). + indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0); + its_translate(its, -0.5f * Vec3f::Ones()); #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GIZMO_GRABBER_REFACTOR s_cube.init_from(its); @@ -71,9 +71,9 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo #else m_cube.init_from(its, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } }); #endif // ENABLE_GIZMO_GRABBER_REFACTOR -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } - +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + } + #if ENABLE_GIZMO_GRABBER_REFACTOR if (!s_cone.is_initialized()) s_cone.init_from(its_make_cone(0.375, 1.5, double(PI) / 18.0)); @@ -83,8 +83,8 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo const float half_size = dragging ? get_dragging_half_size(size) : get_half_size(size); #else const float fullsize = 2.0f * (dragging ? get_dragging_half_size(size) : get_half_size(size)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR - +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GIZMO_GRABBER_REFACTOR s_cube.set_color(render_color); @@ -99,32 +99,32 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo #else m_cube.set_color(-1, render_color); #endif // ENABLE_GIZMO_GRABBER_REFACTOR -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); #if ENABLE_GIZMO_GRABBER_REFACTOR const Transform3d view_model_matrix = camera.get_view_matrix() * matrix * Geometry::assemble_transform(center, angles, 2.0 * half_size * Vec3d::Ones()); #else const Transform3d view_model_matrix = camera.get_view_matrix() * matrix * Geometry::assemble_transform(center, angles, fullsize * Vec3d::Ones()); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR - const Transform3d& projection_matrix = camera.get_projection_matrix(); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", projection_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(center.x(), center.y(), center.z())); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); +#endif // ENABLE_GIZMO_GRABBER_REFACTOR + const Transform3d& projection_matrix = camera.get_projection_matrix(); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", projection_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); +#else + glsafe(::glPushMatrix()); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); #if ENABLE_GIZMO_GRABBER_REFACTOR glsafe(::glScaled(2.0 * half_size, 2.0 * half_size, 2.0 * half_size)); #else glsafe(::glScaled(fullsize, fullsize, fullsize)); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR -#endif // ENABLE_GL_SHADERS_ATTRIBUTES +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#endif // ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GIZMO_GRABBER_REFACTOR s_cube.render(); @@ -198,234 +198,234 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color, boo #endif // ENABLE_GL_SHADERS_ATTRIBUTES #else m_cube.render(); -#endif // ENABLE_GIZMO_GRABBER_REFACTOR -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif // !ENABLE_GL_SHADERS_ATTRIBUTES -} - -GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : m_parent(parent) - , m_group_id(-1) - , m_state(Off) - , m_shortcut_key(0) - , m_icon_filename(icon_filename) - , m_sprite_id(sprite_id) - , m_hover_id(-1) - , m_dragging(false) - , m_imgui(wxGetApp().imgui()) - , m_first_input_window_render(true) - , m_dirty(false) -{ -} - -void GLGizmoBase::set_hover_id(int id) -{ - // do not change hover id during dragging - assert(!m_dragging); - - // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate - if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) - return; - - m_hover_id = id; - on_set_hover_id(); -} - -bool GLGizmoBase::update_items_state() -{ - bool res = m_dirty; - m_dirty = false; - return res; -} - -ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const -{ - id = BASE_ID - id; - if (m_group_id > -1) - id -= m_group_id; - - return picking_decode(id); -} - -void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const -{ - render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); -} - -void GLGizmoBase::render_grabbers(float size) 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) { - if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i, size); - } - shader->stop_using(); -} - -void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const -{ -#if ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { - if (m_grabbers[i].enabled) { - m_grabbers[i].color = picking_color_component(i); - m_grabbers[i].render_for_picking(mean_size); - } - } -#if ENABLE_LEGACY_OPENGL_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -} - -// help function to process grabbers -// call start_dragging, stop_dragging, on_dragging -bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { - bool is_dragging_finished = false; - if (mouse_event.Moving()) { - // it should not happen but for sure - assert(!m_dragging); - if (m_dragging) is_dragging_finished = true; - else return false; - } - - if (mouse_event.LeftDown()) { - Selection &selection = m_parent.get_selection(); - if (!selection.is_empty() && m_hover_id != -1 && - (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { - selection.setup_cache(); - - m_dragging = true; - for (auto &grabber : m_grabbers) grabber.dragging = false; - if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) - m_grabbers[m_hover_id].dragging = true; - - // prevent change of hover_id during dragging - m_parent.set_mouse_as_dragging(); - on_start_dragging(); - - // Let the plater know that the dragging started - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); - m_parent.set_as_dirty(); - return true; - } - } else if (m_dragging) { - // when mouse cursor leave window than finish actual dragging operation - bool is_leaving = mouse_event.Leaving(); - if (mouse_event.Dragging()) { - m_parent.set_mouse_as_dragging(); - Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); - auto ray = m_parent.mouse_ray(mouse_coord); - UpdateData data(ray, mouse_coord); - - on_dragging(data); - - wxGetApp().obj_manipul()->set_dirty(); - m_parent.set_as_dirty(); - return true; - } - else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) { -#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED - do_stop_dragging(is_leaving); -#else - for (auto &grabber : m_grabbers) grabber.dragging = false; - m_dragging = false; - - // NOTE: This should be part of GLCanvas3D - // Reset hover_id when leave window - if (is_leaving) m_parent.mouse_up_cleanup(); - - on_stop_dragging(); - - // There is prediction that after draggign, data are changed - // Data are updated twice also by canvas3D::reload_scene. - // Should be fixed. - m_parent.get_gizmos_manager().update_data(); - - wxGetApp().obj_manipul()->set_dirty(); - - // Let the plater know that the dragging finished, so a delayed - // refresh of the scene with the background processing data should - // be performed. - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // updates camera target constraints - m_parent.refresh_camera_scene_box(); -#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED - return true; - } - } - return false; -} - -#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED -void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup) -{ - for (auto& grabber : m_grabbers) grabber.dragging = false; - m_dragging = false; - - // NOTE: This should be part of GLCanvas3D - // Reset hover_id when leave window - if (perform_mouse_cleanup) m_parent.mouse_up_cleanup(); - - on_stop_dragging(); - - // There is prediction that after draggign, data are changed - // Data are updated twice also by canvas3D::reload_scene. - // Should be fixed. - m_parent.get_gizmos_manager().update_data(); - - wxGetApp().obj_manipul()->set_dirty(); - - // Let the plater know that the dragging finished, so a delayed - // refresh of the scene with the background processing data should - // be performed. - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // updates camera target constraints - m_parent.refresh_camera_scene_box(); -} -#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED - -std::string GLGizmoBase::format(float value, unsigned int decimals) const -{ - return Slic3r::string_printf("%.*f", decimals, value); -} - -void GLGizmoBase::set_dirty() { - m_dirty = true; -} - -void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) -{ - on_render_input_window(x, y, bottom_limit); - if (m_first_input_window_render) { - // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame - // so, we forces another frame rendering the first time the imgui window is shown - m_parent.set_as_dirty(); - m_first_input_window_render = false; - } -} - - - -std::string GLGizmoBase::get_name(bool include_shortcut) const -{ - int key = get_shortcut_key(); - std::string out = on_get_name(); - if (include_shortcut && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z) - out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; - return out; -} - - -} // namespace GUI -} // namespace Slic3r +#endif // ENABLE_GIZMO_GRABBER_REFACTOR +#if !ENABLE_GL_SHADERS_ATTRIBUTES + glsafe(::glPopMatrix()); +#endif // !ENABLE_GL_SHADERS_ATTRIBUTES +} + +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : m_parent(parent) + , m_group_id(-1) + , m_state(Off) + , m_shortcut_key(0) + , m_icon_filename(icon_filename) + , m_sprite_id(sprite_id) + , m_hover_id(-1) + , m_dragging(false) + , m_imgui(wxGetApp().imgui()) + , m_first_input_window_render(true) + , m_dirty(false) +{ +} + +void GLGizmoBase::set_hover_id(int id) +{ + // do not change hover id during dragging + assert(!m_dragging); + + // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate + if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) + return; + + m_hover_id = id; + on_set_hover_id(); +} + +bool GLGizmoBase::update_items_state() +{ + bool res = m_dirty; + m_dirty = false; + return res; +} + +ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const +{ + id = BASE_ID - id; + if (m_group_id > -1) + id -= m_group_id; + + return picking_decode(id); +} + +void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const +{ + render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0)); +} + +void GLGizmoBase::render_grabbers(float size) 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) { + if (m_grabbers[i].enabled) + m_grabbers[i].render(m_hover_id == i, size); + } + shader->stop_using(); +} + +void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const +{ +#if ENABLE_LEGACY_OPENGL_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + const float mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); + + for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { + if (m_grabbers[i].enabled) { + m_grabbers[i].color = picking_color_component(i); + m_grabbers[i].render_for_picking(mean_size); + } + } +#if ENABLE_LEGACY_OPENGL_REMOVAL + shader->stop_using(); + } +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +// help function to process grabbers +// call start_dragging, stop_dragging, on_dragging +bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { + bool is_dragging_finished = false; + if (mouse_event.Moving()) { + // it should not happen but for sure + assert(!m_dragging); + if (m_dragging) is_dragging_finished = true; + else return false; + } + + if (mouse_event.LeftDown()) { + Selection &selection = m_parent.get_selection(); + if (!selection.is_empty() && m_hover_id != -1 && + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { + selection.setup_cache(); + + m_dragging = true; + for (auto &grabber : m_grabbers) grabber.dragging = false; + if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) + m_grabbers[m_hover_id].dragging = true; + + // prevent change of hover_id during dragging + m_parent.set_mouse_as_dragging(); + on_start_dragging(); + + // Let the plater know that the dragging started + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); + m_parent.set_as_dirty(); + return true; + } + } else if (m_dragging) { + // when mouse cursor leave window than finish actual dragging operation + bool is_leaving = mouse_event.Leaving(); + if (mouse_event.Dragging()) { + m_parent.set_mouse_as_dragging(); + Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + auto ray = m_parent.mouse_ray(mouse_coord); + UpdateData data(ray, mouse_coord); + + on_dragging(data); + + wxGetApp().obj_manipul()->set_dirty(); + m_parent.set_as_dirty(); + return true; + } + else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) { +#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED + do_stop_dragging(is_leaving); +#else + for (auto &grabber : m_grabbers) grabber.dragging = false; + m_dragging = false; + + // NOTE: This should be part of GLCanvas3D + // Reset hover_id when leave window + if (is_leaving) m_parent.mouse_up_cleanup(); + + on_stop_dragging(); + + // There is prediction that after draggign, data are changed + // Data are updated twice also by canvas3D::reload_scene. + // Should be fixed. + m_parent.get_gizmos_manager().update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + + // Let the plater know that the dragging finished, so a delayed + // refresh of the scene with the background processing data should + // be performed. + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + m_parent.refresh_camera_scene_box(); +#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED + return true; + } + } + return false; +} + +#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED +void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup) +{ + for (auto& grabber : m_grabbers) grabber.dragging = false; + m_dragging = false; + + // NOTE: This should be part of GLCanvas3D + // Reset hover_id when leave window + if (perform_mouse_cleanup) m_parent.mouse_up_cleanup(); + + on_stop_dragging(); + + // There is prediction that after draggign, data are changed + // Data are updated twice also by canvas3D::reload_scene. + // Should be fixed. + m_parent.get_gizmos_manager().update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + + // Let the plater know that the dragging finished, so a delayed + // refresh of the scene with the background processing data should + // be performed. + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + m_parent.refresh_camera_scene_box(); +} +#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED + +std::string GLGizmoBase::format(float value, unsigned int decimals) const +{ + return Slic3r::string_printf("%.*f", decimals, value); +} + +void GLGizmoBase::set_dirty() { + m_dirty = true; +} + +void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) +{ + on_render_input_window(x, y, bottom_limit); + if (m_first_input_window_render) { + // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame + // so, we forces another frame rendering the first time the imgui window is shown + m_parent.set_as_dirty(); + m_first_input_window_render = false; + } +} + + + +std::string GLGizmoBase::get_name(bool include_shortcut) const +{ + int key = get_shortcut_key(); + std::string out = on_get_name(); + if (include_shortcut && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z) + out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; + return out; +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 15030a0fa7..8ef23d5382 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -126,7 +126,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #else const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); #endif // ENABLE_TRANSFORMATIONS_BY_MATRICES - const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * trafo.get_matrix(); + const Transform3d instance_matrix = Geometry::translation_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * trafo.get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d& view_matrix = camera.get_view_matrix(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index c0c12dd5a5..2b53f71ebb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -141,7 +141,19 @@ void GLGizmoMove3D::on_dragging(const UpdateData& data) Selection &selection = m_parent.get_selection(); #if ENABLE_WORLD_COORDINATE +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + TransformationType trafo_type; + trafo_type.set_relative(); + switch (wxGetApp().obj_manipul()->get_coordinates_type()) + { + case ECoordinatesType::Instance: { trafo_type.set_instance(); break; } + case ECoordinatesType::Local: { trafo_type.set_local(); break; } + default: { break; } + } + selection.translate(m_displacement, trafo_type); +#else selection.translate(m_displacement, wxGetApp().obj_manipul()->get_coordinates_type()); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else selection.translate(m_displacement); #endif // ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index f9368d5567..b7da8a3529 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -787,7 +787,7 @@ Transform3d GLGizmoRotate::local_transform(const Selection& selection) const } #if ENABLE_WORLD_COORDINATE - return Geometry::assemble_transform(m_center) * m_orient_matrix * ret; + return Geometry::translation_transform(m_center) * m_orient_matrix * ret; #else if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes()) ret = selection.get_first_volume()->get_instance_transformation().get_matrix(true, false, true, true) * ret; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 1ce4976113..a6e1b73cdf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -41,6 +41,9 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filen std::string GLGizmoScale3D::get_tooltip() const { +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + const Vec3d scale = 100.0 * m_scale; +#else const Selection& selection = m_parent.get_selection(); Vec3d scale = 100.0 * Vec3d::Ones(); @@ -52,6 +55,7 @@ std::string GLGizmoScale3D::get_tooltip() const else if (selection.is_single_modifier() || selection.is_single_volume()) #endif // ENABLE_WORLD_COORDINATE scale = 100.0 * selection.get_first_volume()->get_volume_scaling_factor(); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES if (m_hover_id == 0 || m_hover_id == 1 || m_grabbers[0].dragging || m_grabbers[1].dragging) return "X: " + format(scale.x(), 4) + "%"; @@ -91,33 +95,48 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event) // Apply new temporary scale factors #if ENABLE_WORLD_COORDINATE TransformationType transformation_type; +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + if (wxGetApp().obj_manipul()->is_local_coordinates()) + transformation_type.set_local(); + else if (wxGetApp().obj_manipul()->is_instance_coordinates()) + transformation_type.set_instance(); + + transformation_type.set_relative(); +#else if (!wxGetApp().obj_manipul()->is_world_coordinates()) transformation_type.set_local(); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else TransformationType transformation_type(TransformationType::Local_Absolute_Joint); #endif // ENABLE_WORLD_COORDINATE if (mouse_event.AltDown()) transformation_type.set_independent(); - Selection &selection = m_parent.get_selection(); +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + m_parent.get_selection().scale_and_translate(m_scale, m_offset, transformation_type); +#else + Selection& selection = m_parent.get_selection(); selection.scale(m_scale, transformation_type); #if ENABLE_WORLD_COORDINATE if (mouse_event.CmdDown()) selection.translate(m_offset, wxGetApp().obj_manipul()->get_coordinates_type()); #else if (mouse_event.CmdDown()) selection.translate(m_offset, true); #endif // ENABLE_WORLD_COORDINATE +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #if !ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_WORLD_COORDINATE_SCALE_REVISITED } #endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED #endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES - } + } } return use_grabbers(mouse_event); } void GLGizmoScale3D::data_changed() { +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES const Selection &selection = m_parent.get_selection(); +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_WORLD_COORDINATE #if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED bool enable_scale_xyz = !selection.requires_uniform_scale(); @@ -127,6 +146,9 @@ void GLGizmoScale3D::data_changed() selection.is_single_volume() || selection.is_single_modifier(); #endif // ENABLE_WORLD_COORDINATE +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + set_scale(Vec3d::Ones()); +#else #if ENABLE_WORLD_COORDINATE_SCALE_REVISITED if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { #else @@ -151,6 +173,7 @@ void GLGizmoScale3D::data_changed() } else set_scale(Vec3d::Ones()); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES } bool GLGizmoScale3D::on_init() @@ -268,7 +291,7 @@ void GLGizmoScale3D::on_render() #if ENABLE_WORLD_COORDINATE #if ENABLE_TRANSFORMATIONS_BY_MATRICES const Transform3d inst_trafo = v.get_instance_transformation().get_matrix_no_scaling_factor(); - m_grabbers_transform = inst_trafo * Geometry::assemble_transform(m_bounding_box.center()); + m_grabbers_transform = inst_trafo * Geometry::translation_transform(m_bounding_box.center()); m_center = inst_trafo * m_bounding_box.center(); #else m_grabbers_transform = v.get_instance_transformation().get_matrix(false, false, true) * Geometry::assemble_transform(m_bounding_box.center()); @@ -698,6 +721,58 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int } #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +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 Selection& selection = m_parent.get_selection(); + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + + curr_scale(axis) = starting_scale(axis) * ratio; + m_scale = curr_scale; + + if (m_starting.ctrl_down && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier())) { + double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); + + 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; } + case Y: { m_offset = local_offset * Vec3d::UnitY(); break; } + case Z: { m_offset = local_offset * Vec3d::UnitZ(); break; } + 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; + else if (coordinates_type == ECoordinatesType::Local) { + m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * + selection.get_first_volume()->get_volume_transformation().get_rotation_matrix() * m_offset; + } + } + } + else + m_offset = Vec3d::Zero(); + } +} +#else void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) { #if ENABLE_WORLD_COORDINATE @@ -742,6 +817,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (ratio > 0.0) { m_scale(axis) = m_starting.scale(axis) * ratio; #endif // ENABLE_WORLD_COORDINATE + if (m_starting.ctrl_down) { #if ENABLE_WORLD_COORDINATE double local_offset = 0.5 * (ratio - 1.0) * m_starting.box.size()(axis); @@ -755,7 +831,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) #if ENABLE_WORLD_COORDINATE Vec3d center_offset = m_starting.instance_center - m_starting.center; if (selection.is_single_full_instance() && coordinates_type != ECoordinatesType::World) { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_first_volume()->get_instance_rotation()).inverse(); + const Transform3d m = Geometry::rotation_transform(selection.get_first_volume()->get_instance_rotation()).inverse(); center_offset = m * center_offset; } @@ -785,12 +861,57 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) m_offset = Vec3d::Zero(); } } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +void GLGizmoScale3D::do_scale_uniform(const UpdateData & data) +{ + const double ratio = calc_ratio(data); + if (ratio > 0.0) { + m_scale = m_starting.scale * ratio; + + const Selection& selection = m_parent.get_selection(); + const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); + if (m_starting.ctrl_down && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier())) { + m_offset = 0.5 * (ratio - 1.0) * m_starting.box.size(); + + if (m_hover_id == 6 || m_hover_id == 9) + m_offset.x() *= -1.0; + 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; + else if (coordinates_type == ECoordinatesType::Local) { + m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * + selection.get_first_volume()->get_volume_transformation().get_rotation_matrix() * m_offset; + } + } + } + else + m_offset = Vec3d::Zero(); + } +} +#else void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) { const double ratio = calc_ratio(data); if (ratio > 0.0) { m_scale = m_starting.scale * ratio; + #if ENABLE_WORLD_COORDINATE if (m_starting.ctrl_down) { m_offset = 0.5 * (ratio - 1.0) * m_starting.box.size(); @@ -804,7 +925,7 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) Vec3d center_offset = m_starting.instance_center - m_starting.center; if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_first_volume()->get_instance_rotation()).inverse(); + const Transform3d m = Geometry::rotation_transform(selection.get_first_volume()->get_instance_rotation()).inverse(); center_offset = m * center_offset; } @@ -815,6 +936,7 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data) m_offset = Vec3d::Zero(); } } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES double GLGizmoScale3D::calc_ratio(const UpdateData& data) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 32a02d512e..1a8c5e5df3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -69,7 +69,11 @@ public: void set_snap_step(double step) { m_snap_step = step; } const Vec3d& get_scale() const { return m_scale; } +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; m_offset = Vec3d::Zero(); } +#else void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES std::string get_tooltip() const override; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8784a82920..4dbf46fd6a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3849,7 +3849,7 @@ void Plater::priv::reload_from_disk() new_volume->set_type(old_volume->type()); new_volume->set_material_id(old_volume->material_id()); #if ENABLE_TRANSFORMATIONS_BY_MATRICES - new_volume->set_transformation(Geometry::assemble_transform(old_volume->source.transform.get_offset()) * + new_volume->set_transformation(Geometry::translation_transform(old_volume->source.transform.get_offset()) * old_volume->get_transformation().get_matrix_no_offset() * old_volume->source.transform.get_matrix_no_offset()); new_volume->translate(new_volume->get_transformation().get_matrix_no_offset() * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); #else diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2b2f19c15c..97daa7a56a 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -598,6 +598,7 @@ bool Selection::matches(const std::vector& volume_idxs) const return count == (unsigned int)m_list.size(); } +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_WORLD_COORDINATE bool Selection::requires_uniform_scale(EUniformScaleRequiredReason* reason) const #else @@ -666,6 +667,7 @@ bool Selection::requires_uniform_scale() const return true; #endif // ENABLE_WORLD_COORDINATE } +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES int Selection::get_object_idx() const { @@ -759,58 +761,29 @@ void Selection::setup_cache() } #if ENABLE_TRANSFORMATIONS_BY_MATRICES -void Selection::translate(const Vec3d& displacement, ECoordinatesType type) +void Selection::translate(const Vec3d& displacement, TransformationType transformation_type) { if (!m_valid) return; + assert(transformation_type.relative()); + for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; const VolumeCache& volume_data = m_cache.volumes_data[i]; if (m_mode == Instance && !is_wipe_tower()) { assert(is_from_fully_selected_instance(i)); - switch (type) - { - case ECoordinatesType::World: - { - v.set_instance_transformation(Geometry::assemble_transform(displacement) * volume_data.get_instance_full_matrix()); - break; - } - case ECoordinatesType::Local: - { - const Vec3d world_displacemet = volume_data.get_instance_rotation_matrix() * displacement; - v.set_instance_transformation(Geometry::assemble_transform(world_displacemet) * volume_data.get_instance_full_matrix()); - break; - } - default: { assert(false); break; } - } - } - else { - switch (type) - { - case ECoordinatesType::World: - { - const Transform3d inst_matrix_no_offset = volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix(); - const Vec3d inst_displacement = inst_matrix_no_offset.inverse() * displacement; - v.set_volume_transformation(Geometry::assemble_transform(inst_displacement) * volume_data.get_volume_full_matrix()); - break; - } - case ECoordinatesType::Instance: - { - const Vec3d inst_displacement = volume_data.get_instance_scale_matrix().inverse() * displacement; - v.set_volume_transformation(Geometry::assemble_transform(inst_displacement) * volume_data.get_volume_full_matrix()); - break; - } - case ECoordinatesType::Local: - { - const Vec3d inst_displacement = volume_data.get_instance_scale_matrix().inverse() * - volume_data.get_volume_rotation_matrix() * displacement; - v.set_volume_transformation(Geometry::assemble_transform(inst_displacement) * volume_data.get_volume_full_matrix()); - break; - } - default: { assert(false); break; } + if (transformation_type.world()) + v.set_instance_transformation(Geometry::translation_transform(displacement) * volume_data.get_instance_full_matrix()); + else if (transformation_type.local()) { + const Vec3d world_displacement = volume_data.get_instance_rotation_matrix() * displacement; + v.set_instance_transformation(Geometry::translation_transform(world_displacement) * volume_data.get_instance_full_matrix()); } + else + assert(false); } + else + transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(displacement)); } #if !DISABLE_INSTANCES_SYNCH @@ -913,51 +886,35 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ 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(); if (m_mode == Instance && !is_wipe_tower()) { assert(is_from_fully_selected_instance(i)); - const Geometry::Transformation& old_trafo = volume_data.get_instance_transform(); Transform3d new_rotation_matrix = Transform3d::Identity(); if (transformation_type.absolute()) new_rotation_matrix = rotation_matrix; else { if (transformation_type.world()) - new_rotation_matrix = rotation_matrix * old_trafo.get_rotation_matrix(); + new_rotation_matrix = rotation_matrix * inst_trafo.get_rotation_matrix(); else if (transformation_type.local()) - new_rotation_matrix = old_trafo.get_rotation_matrix() * rotation_matrix; + new_rotation_matrix = inst_trafo.get_rotation_matrix() * rotation_matrix; else assert(false); } - const Vec3d new_offset = transformation_type.independent() ? old_trafo.get_offset() : - m_cache.dragging_center + new_rotation_matrix * old_trafo.get_rotation_matrix().inverse() * - (old_trafo.get_offset() - m_cache.dragging_center); - v.set_instance_transformation(Geometry::assemble_transform(Geometry::assemble_transform(new_offset), new_rotation_matrix, - old_trafo.get_scaling_factor_matrix(), old_trafo.get_mirror_matrix())); + const Vec3d new_offset = transformation_type.independent() ? inst_trafo.get_offset() : + m_cache.dragging_center + new_rotation_matrix * inst_trafo.get_rotation_matrix().inverse() * + (inst_trafo.get_offset() - m_cache.dragging_center); + v.set_instance_transformation(Geometry::assemble_transform(Geometry::translation_transform(new_offset), new_rotation_matrix, + inst_trafo.get_scaling_factor_matrix(), inst_trafo.get_mirror_matrix())); } else { - const Geometry::Transformation& old_trafo = volume_data.get_volume_transform(); - Transform3d new_rotation_matrix = Transform3d::Identity(); - - if (transformation_type.absolute()) - new_rotation_matrix = rotation_matrix; - else { - if (transformation_type.world()) { - const Transform3d inst_rotation_matrix = volume_data.get_instance_transform().get_rotation_matrix(); - new_rotation_matrix = inst_rotation_matrix.inverse() * rotation_matrix * inst_rotation_matrix * old_trafo.get_rotation_matrix(); - } - else if (transformation_type.instance()) - new_rotation_matrix = rotation_matrix * old_trafo.get_rotation_matrix(); - else if (transformation_type.local()) - new_rotation_matrix = old_trafo.get_rotation_matrix() * rotation_matrix; - else - assert(false); + if (transformation_type.absolute()) { + const Geometry::Transformation& volume_trafo = volume_data.get_volume_transform(); + v.set_volume_transformation(Geometry::assemble_transform(volume_trafo.get_offset_matrix(), Geometry::rotation_transform(rotation), + volume_trafo.get_scaling_factor_matrix(), volume_trafo.get_mirror_matrix())); } - - const Vec3d new_offset = !is_wipe_tower() ? old_trafo.get_offset() : - m_cache.dragging_center + new_rotation_matrix * old_trafo.get_rotation_matrix().inverse() * - (old_trafo.get_offset() - m_cache.dragging_center); - v.set_volume_transformation(Geometry::assemble_transform(Geometry::assemble_transform(new_offset), new_rotation_matrix, - old_trafo.get_scaling_factor_matrix(), old_trafo.get_mirror_matrix())); + else + transform_volume_relative(v, volume_data, transformation_type, Geometry::rotation_transform(rotation)); } } @@ -1181,6 +1138,12 @@ void Selection::flattening_rotate(const Vec3d& normal) this->set_bounding_boxes_dirty(); } +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +void Selection::scale(const Vec3d& scale, TransformationType transformation_type) +{ + scale_and_translate(scale, Vec3d::Zero(), transformation_type); +} +#else void Selection::scale(const Vec3d& scale, TransformationType transformation_type) { if (!m_valid) @@ -1255,6 +1218,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type set_bounding_boxes_dirty(); wxGetApp().plater()->canvas3D()->requires_check_outside_state(); } +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES void Selection::scale_to_fit_print_volume(const BuildVolume& volume) { @@ -1277,7 +1241,13 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume) // center selection on print bed setup_cache(); offset.z() = -get_bounding_box().min.z(); +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + TransformationType trafo_type; + trafo_type.set_relative(); + translate(offset, trafo_type); +#else translate(offset); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot wxGetApp().obj_manipul()->set_dirty(); @@ -1367,7 +1337,78 @@ void Selection::mirror(Axis axis) set_bounding_boxes_dirty(); } -#if !ENABLE_TRANSFORMATIONS_BY_MATRICES +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation, TransformationType transformation_type) +{ + if (!m_valid) + return; + + std::cout << "Selection::scale_and_translate: " << to_string(scale) << " - " << to_string(translation) << "\n"; + + 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(); + if (m_mode == Instance) { + assert(is_from_fully_selected_instance(i)); + if (transformation_type.absolute()) { + assert(transformation_type.local()); + assert(transformation_type.joint()); + v.set_instance_transformation(Geometry::assemble_transform(inst_trafo.get_offset_matrix(), inst_trafo.get_rotation_matrix(), + Geometry::scale_transform(scale), inst_trafo.get_mirror_matrix())); + } + else { + if (transformation_type.world()) { + const Transform3d scale_matrix = Geometry::scale_transform(scale); + const Transform3d offset_matrix = (transformation_type.joint() && translation.isApprox(Vec3d::Zero())) ? + // non-constrained scaling - add offset to scale around selection center + Geometry::translation_transform(m_cache.dragging_center + scale_matrix * (inst_trafo.get_offset() - m_cache.dragging_center)) : + // constrained scaling - add offset to keep constraint + Geometry::translation_transform(translation) * inst_trafo.get_offset_matrix(); + v.set_instance_transformation(offset_matrix * scale_matrix * inst_trafo.get_matrix_no_offset()); + } + else if (transformation_type.local()) { + const Transform3d scale_matrix = Geometry::scale_transform(scale); + Vec3d offset; + if (transformation_type.joint() && translation.isApprox(Vec3d::Zero())) { + // non-constrained scaling - add offset to scale around selection center + offset = inst_trafo.get_matrix_no_offset().inverse() * (inst_trafo.get_offset() - m_cache.dragging_center); + offset = inst_trafo.get_matrix_no_offset() * (scale_matrix * offset - offset); + } + else + // constrained scaling - add offset to keep constraint + offset = translation; + + v.set_instance_transformation(Geometry::translation_transform(offset) * inst_trafo.get_matrix() * scale_matrix); + } + else + assert(false); + } + } + else { + if (transformation_type.absolute()) { + assert(transformation_type.local()); + const Geometry::Transformation& volume_trafo = volume_data.get_volume_transform(); + v.set_volume_transformation(Geometry::assemble_transform(volume_trafo.get_offset_matrix(), volume_trafo.get_rotation_matrix(), + Geometry::scale_transform(scale), volume_trafo.get_mirror_matrix())); + } + else + transform_volume_relative(v, volume_data, transformation_type, Geometry::translation_transform(translation) * Geometry::scale_transform(scale)); + } + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + synchronize_unselected_instances(SyncRotationType::NONE); + else if (m_mode == Volume) + synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + ensure_on_bed(); + set_bounding_boxes_dirty(); + wxGetApp().plater()->canvas3D()->requires_check_outside_state(); +} +#else void Selection::translate(unsigned int object_idx, const Vec3d& displacement) { if (!m_valid) @@ -1416,7 +1457,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) this->set_bounding_boxes_dirty(); } -#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES void Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement) { @@ -1427,7 +1468,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co GLVolume& v = *(*m_volumes)[i]; if (v.object_idx() == (int)object_idx && v.instance_idx() == (int)instance_idx) #if ENABLE_TRANSFORMATIONS_BY_MATRICES - v.set_instance_transformation(Geometry::assemble_transform(displacement) * v.get_instance_transformation().get_matrix()); + v.set_instance_transformation(Geometry::translation_transform(displacement) * v.get_instance_transformation().get_matrix()); #else v.set_instance_offset(v.get_instance_offset() + displacement); #endif // ENABLE_TRANSFORMATIONS_BY_MATRICES @@ -1464,7 +1505,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; #if ENABLE_TRANSFORMATIONS_BY_MATRICES - v.set_instance_transformation(Geometry::assemble_transform(displacement) * v.get_instance_transformation().get_matrix()); + v.set_instance_transformation(Geometry::translation_transform(displacement) * v.get_instance_transformation().get_matrix()); #else v.set_instance_offset(v.get_instance_offset() + displacement); #endif // ENABLE_TRANSFORMATIONS_BY_MATRICES @@ -2764,7 +2805,11 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field, GLS void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) #endif // ENABLE_GL_SHADERS_ATTRIBUTES { +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + const bool uniform_scale = wxGetApp().obj_manipul()->get_uniform_scaling(); +#else const bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #if ENABLE_GL_SHADERS_ATTRIBUTES auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis, GLShaderProgram& shader, const Transform3d& matrix) { @@ -3375,5 +3420,27 @@ void Selection::paste_objects_from_clipboard() #endif /* _DEBUG */ } +#if ENABLE_TRANSFORMATIONS_BY_MATRICES +void Selection::transform_volume_relative(GLVolume& volume, const VolumeCache& volume_data, TransformationType transformation_type, + const Transform3d& transform) +{ + const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform(); + const Geometry::Transformation& volume_trafo = volume_data.get_volume_transform(); + if (transformation_type.world()) { + const Transform3d inst_matrix_no_offset = inst_trafo.get_matrix_no_offset(); + const Transform3d new_volume_matrix = inst_matrix_no_offset.inverse() * transform * inst_matrix_no_offset; + volume.set_volume_transformation(volume_trafo.get_offset_matrix() * new_volume_matrix * volume_trafo.get_matrix_no_offset()); + } + else if (transformation_type.instance()) + volume.set_volume_transformation(volume_trafo.get_offset_matrix() * transform * volume_trafo.get_matrix_no_offset()); + else if (transformation_type.local()) { + const Geometry::Transformation trafo(transform); + volume.set_volume_transformation(trafo.get_offset_matrix() * volume_trafo.get_matrix() * trafo.get_matrix_no_offset()); + } + else + assert(false); +} +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c9f0eb7c68..bd9c9216ca 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -336,7 +336,9 @@ public: VolumeNotAxisAligned_Instance, MultipleSelection, }; +#if !ENABLE_TRANSFORMATIONS_BY_MATRICES bool requires_uniform_scale(EUniformScaleRequiredReason* reason = nullptr) const; +#endif // !ENABLE_TRANSFORMATIONS_BY_MATRICES #else bool requires_uniform_scale() const; #endif // ENABLE_WORLD_COORDINATE @@ -365,7 +367,11 @@ public: void setup_cache(); #if ENABLE_WORLD_COORDINATE +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + void translate(const Vec3d& displacement, TransformationType transformation_type); +#else void translate(const Vec3d& displacement, ECoordinatesType type = ECoordinatesType::World); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #else void translate(const Vec3d& displacement, bool local = false); #endif // ENABLE_WORLD_COORDINATE @@ -374,6 +380,9 @@ public: void scale(const Vec3d& scale, TransformationType transformation_type); void scale_to_fit_print_volume(const BuildVolume& volume); void mirror(Axis axis); +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + void scale_and_translate(const Vec3d& scale, const Vec3d& translation, TransformationType transformation_type); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES #if !ENABLE_TRANSFORMATIONS_BY_MATRICES void translate(unsigned int object_idx, const Vec3d& displacement); @@ -468,6 +477,11 @@ private: void paste_volumes_from_clipboard(); void paste_objects_from_clipboard(); + +#if ENABLE_TRANSFORMATIONS_BY_MATRICES + void transform_volume_relative(GLVolume& volume, const VolumeCache& volume_data, TransformationType transformation_type, + const Transform3d& transform); +#endif // ENABLE_TRANSFORMATIONS_BY_MATRICES }; } // namespace GUI