diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2f31a9171..2cee2c7c4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -293,6 +293,20 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) return { w - thickness_bar_width(canvas), 0.0f, w, h }; } +std::pair> GLCanvas3D::LayersEditing::get_layers_height_data() +{ + if (m_slicing_parameters != nullptr) + return { *m_slicing_parameters, m_layer_height_profile }; + + assert(m_model_object != nullptr); + this->update_slicing_parameters(); + PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile); + std::pair> ret = { *m_slicing_parameters, m_layer_height_profile }; + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + return ret; +} + bool GLCanvas3D::LayersEditing::is_initialized() const { return wxGetApp().get_shader("variable_layer_height") != nullptr; @@ -4082,6 +4096,14 @@ void GLCanvas3D::apply_retina_scale(Vec2d &screen_coordinate) const #endif // ENABLE_RETINA_GL } +std::pair> GLCanvas3D::get_layers_height_data(int object_id) +{ + m_layers_editing.select_object(*m_model, object_id); + std::pair> ret = m_layers_editing.get_layers_height_data(); + m_layers_editing.select_object(*m_model, -1); + return ret; +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8545cdf51..e91a727c5 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -291,6 +291,8 @@ class GLCanvas3D std::string get_tooltip(const GLCanvas3D& canvas) const; + std::pair> get_layers_height_data(); + private: bool is_initialized() const; void generate_layer_height_texture(); @@ -958,6 +960,8 @@ public: void apply_retina_scale(Vec2d &screen_coordinate) const; + std::pair> get_layers_height_data(int object_id); + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index fc2a6e0ea..0841d61a1 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1302,11 +1302,12 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type void Selection::scale_to_fit_print_volume(const BuildVolume& volume) { - auto fit = [this](double s, Vec3d offset) { + auto fit = [this](double s, Vec3d offset, bool undoredo_snapshot) { if (s <= 0.0 || s == 1.0) - return; + return false; - wxGetApp().plater()->take_snapshot(_L("Scale To Fit")); + if (undoredo_snapshot) + wxGetApp().plater()->take_snapshot(_L("Scale To Fit")); TransformationType type; type.set_world(); @@ -1331,29 +1332,32 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume) wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot wxGetApp().obj_manipul()->set_dirty(); + return undoredo_snapshot; }; - auto fit_rectangle = [this, fit](const BuildVolume& volume) { + auto fit_rectangle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) { const BoundingBoxf3 print_volume = volume.bounding_volume(); - const Vec3d print_volume_size = print_volume.size(); + Vec3d print_volume_size = print_volume.size(); + print_volume_size.z() = (max_height != nullptr) ? *max_height : volume.max_print_height(); - // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings - const Vec3d box_size = get_bounding_box().size() + 0.02 * Vec3d::Ones(); + // adds 1/100th of a mm on both xy sides to avoid false out of print volume detections due to floating-point roundings + Vec3d box_size = get_bounding_box().size(); + box_size.x() += 0.02; + box_size.y() += 0.02; - const double sx = (box_size.x() != 0.0) ? print_volume_size.x() / box_size.x() : 0.0; - const double sy = (box_size.y() != 0.0) ? print_volume_size.y() / box_size.y() : 0.0; - const double sz = (box_size.z() != 0.0) ? print_volume_size.z() / box_size.z() : 0.0; + const double sx = print_volume_size.x() / box_size.x(); + const double sy = print_volume_size.y() / box_size.y(); + const double sz = print_volume_size.z() / box_size.z(); - if (sx != 0.0 && sy != 0.0 && sz != 0.0) - fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center()); + return fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center(), undoredo_snapshot); }; - auto fit_circle = [this, fit](const BuildVolume& volume) { + auto fit_circle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) { const Geometry::Circled& print_circle = volume.circle(); double print_circle_radius = unscale(print_circle.radius); if (print_circle_radius == 0.0) - return; + return false; Points points; double max_z = 0.0; @@ -1367,31 +1371,56 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume) } if (points.empty()) - return; + return false; const Geometry::Circled circle = Geometry::smallest_enclosing_circle_welzl(points); // adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings const double circle_radius = unscale(circle.radius) + 0.01; if (circle_radius == 0.0 || max_z == 0.0) - return; + return false; - const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / max_z); + const double print_volume_max_z = (max_height != nullptr) ? *max_height : volume.max_print_height(); + const double s = std::min(print_circle_radius / circle_radius, print_volume_max_z / max_z); const Vec3d sel_center = get_bounding_box().center(); const Vec3d offset = s * (Vec3d(unscale(circle.center.x()), unscale(circle.center.y()), 0.5 * max_z) - sel_center); const Vec3d print_center = { unscale(print_circle.center.x()), unscale(print_circle.center.y()), 0.5 * volume.max_print_height() }; - fit(s, print_center - (sel_center + offset)); + return fit(s, print_center - (sel_center + offset), undoredo_snapshot); }; if (is_empty() || m_mode == Volume) return; + assert(is_single_full_instance()); + + // used to keep track whether the undo/redo snapshot has already been taken + bool undoredo_snapshot = false; + switch (volume.type()) { - case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; } - case BuildVolume::Type::Circle: { fit_circle(volume); break; } + case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; } + case BuildVolume::Type::Circle: { undoredo_snapshot = fit_circle(volume, !undoredo_snapshot); break; } default: { break; } } + + if (wxGetApp().plater()->printer_technology() == ptFFF) { + // check whether the top layer exceeds the maximum height of the print volume + // and, in case, reduce the scale accordingly + const auto [slicing_parameters, profile] = wxGetApp().plater()->canvas3D()->get_layers_height_data(get_object_idx()); + auto layers = generate_object_layers(slicing_parameters, profile); + auto layers_it = layers.rbegin(); + while (layers_it != layers.rend() && *layers_it > volume.bounding_volume().max.z()) { + ++layers_it; + } + if (layers_it != layers.rbegin() && layers_it != layers.rend()) { + switch (volume.type()) + { + case BuildVolume::Type::Rectangle: { fit_rectangle(volume, !undoredo_snapshot, &(*layers_it)); break; } + case BuildVolume::Type::Circle: { fit_circle(volume, !undoredo_snapshot, &(*layers_it)); break; } + default: { break; } + } + } + } } #if ENABLE_WORLD_COORDINATE