diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 39f1fa4c7..3b288198e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -91,5 +91,8 @@ // an additional button can be used to set the keyboard focus into the slider // to allow the user to type in the desired value #define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2) +// Enable fit print volume command for circular printbeds +#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2) + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 3671ee130..588d02cbc 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -786,8 +786,14 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) void MenuFactory::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu) { +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT + append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"), + [](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu, + []() { return plater()->can_scale_to_print_volume(); }, m_parent); +#else append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"), [](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu); +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT } void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9049bbf38..4e60f09e2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1910,6 +1910,9 @@ struct Plater::priv bool can_reload_from_disk() const; bool can_replace_with_stl() const; bool can_split(bool to_objects) const; +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT + bool can_scale_to_print_volume() const; +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type); ThumbnailsList generate_thumbnails(const ThumbnailsParams& params, Camera::EType camera_type); @@ -3075,7 +3078,11 @@ void Plater::priv::split_volume() void Plater::priv::scale_selection_to_fit_print_volume() { +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT + this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(this->bed.build_volume()); +#else this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(*config); +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT } void Plater::priv::schedule_background_process() @@ -4543,6 +4550,14 @@ bool Plater::priv::can_split(bool to_objects) const return sidebar->obj_list()->is_splittable(to_objects); } +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT +bool Plater::priv::can_scale_to_print_volume() const +{ + const BuildVolume::Type type = this->bed.build_volume().type(); + return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle); +} +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT + bool Plater::priv::layers_height_allowed() const { if (printer_technology != ptFFF) @@ -6872,6 +6887,10 @@ bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); } bool Plater::can_replace_with_stl() const { return p->can_replace_with_stl(); } bool Plater::can_mirror() const { return p->can_mirror(); } bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); } +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT +bool Plater::can_scale_to_print_volume() const { return p->can_scale_to_print_volume(); } +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT + const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); } void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); } void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 19bd67eda..55a36d21c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -322,6 +322,9 @@ public: bool can_replace_with_stl() const; bool can_mirror() const; bool can_split(bool to_objects) const; +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT + bool can_scale_to_print_volume() const; +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT void msw_rescale(); void sys_color_changed(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c3313d434..c596b0c2c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -15,6 +15,9 @@ #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT +#include "libslic3r/BuildVolume.hpp" +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT #include @@ -948,6 +951,94 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type wxGetApp().plater()->canvas3D()->requires_check_outside_state(); } +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT +void Selection::scale_to_fit_print_volume(const BuildVolume& volume) +{ + auto fit = [this](double s, const Vec3d& offset) { + if (s <= 0.0 || s == 1.0) + return; + + wxGetApp().plater()->take_snapshot(_L("Scale To Fit")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + start_dragging(); + scale(s * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + + // center selection on print bed + start_dragging(); + translate(offset); + wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot + + wxGetApp().obj_manipul()->set_dirty(); + }; + + auto fit_rectangle = [this, fit](const BuildVolume& volume) { + const BoundingBoxf3 print_volume = volume.bounding_volume(); + const Vec3d print_volume_size = print_volume.size(); + + // 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(); + + 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; + + 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()); + }; + + auto fit_circle = [this, fit](const BuildVolume& volume) { + const Geometry::Circled& print_circle = volume.circle(); + double print_circle_radius = unscale(print_circle.radius); + + if (print_circle_radius == 0.0) + return; + + Points points; + double max_z = 0.0; + for (unsigned int i : m_list) { + const GLVolume& v = *(*m_volumes)[i]; + TriangleMesh hull_3d = *v.convex_hull(); + hull_3d.transform(v.world_matrix()); + max_z = std::max(max_z, hull_3d.bounding_box().size().z()); + const Polygon hull_2d = hull_3d.convex_hull(); + points.insert(points.end(), hull_2d.begin(), hull_2d.end()); + } + + if (points.empty()) + return; + + 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; + + const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / 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)); + }; + + if (is_empty() || m_mode == Volume) + return; + + switch (volume.type()) + { + case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; } + case BuildVolume::Type::Circle: { fit_circle(volume); break; } + default: { break; } + } +} +#else void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) { if (is_empty() || m_mode == Volume) @@ -990,6 +1081,7 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) } } } +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT void Selection::mirror(Axis axis) { diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c81fa0510..c18d24eab 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -17,6 +17,9 @@ class GLArrow; class GLCurvedArrow; class DynamicPrintConfig; class GLShaderProgram; +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT +class BuildVolume; +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT using GLVolumePtrs = std::vector; using ModelObjectPtrs = std::vector; @@ -320,7 +323,11 @@ public: void rotate(const Vec3d& rotation, TransformationType transformation_type); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, TransformationType transformation_type); +#if ENABLE_ENHANCED_PRINT_VOLUME_FIT + void scale_to_fit_print_volume(const BuildVolume& volume); +#else void scale_to_fit_print_volume(const DynamicPrintConfig& config); +#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT void mirror(Axis axis); void translate(unsigned int object_idx, const Vec3d& displacement);