diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index bf2a219d0..254ebf2ef 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -85,7 +85,13 @@ template void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { // Align the arranged pile into the center of the bin - pcfg.alignment = PConf::Alignment::CENTER; + switch (params.alignment) { + case Pivots::Center: pcfg.alignment = PConf::Alignment::CENTER; break; + case Pivots::BottomLeft: pcfg.alignment = PConf::Alignment::BOTTOM_LEFT; break; + case Pivots::BottomRight: pcfg.alignment = PConf::Alignment::BOTTOM_RIGHT; break; + case Pivots::TopLeft: pcfg.alignment = PConf::Alignment::TOP_LEFT; break; + case Pivots::TopRight: pcfg.alignment = PConf::Alignment::TOP_RIGHT; break; + } // Start placing the items from the center of the print bed pcfg.starting_point = PConf::Alignment::CENTER; @@ -613,6 +619,12 @@ template auto call_with_bed(const Points &bed, Fn &&fn) } } +bool is_box(const Points &bed) +{ + return !bed.empty() && + ((1.0 - poly_area(bed) / area(BoundingBox(bed))) < 1e-3); +} + template<> void arrange(ArrangePolygons & items, const ArrangePolygons &excludes, diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 0ff87c88d..e56ecfbe2 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -75,6 +75,10 @@ struct ArrangePolygon { using ArrangePolygons = std::vector; +enum class Pivots { + Center, TopLeft, BottomLeft, BottomRight, TopRight +}; + struct ArrangeParams { /// The minimum distance which is allowed for any @@ -89,7 +93,13 @@ struct ArrangeParams { bool parallel = true; bool allow_rotations = false; - + + /// Final alignment of the merged pile after arrangement + Pivots alignment = Pivots::Center; + + /// Starting position hint for the arrangement + Pivots starting_point = Pivots::Center; + /// Progress indicator callback called when an object gets packed. /// The unsigned argument is the number of items remaining to pack. std::function progressind; @@ -130,6 +140,8 @@ inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeP inline void arrange(ArrangePolygons &items, const Polygon &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } inline void arrange(ArrangePolygons &items, const InfiniteBed &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } +bool is_box(const Points &bed); + }} // namespace Slic3r::arrangement #endif // MODELARRANGE_HPP diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b1410c188..e4c09fb25 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -931,6 +931,19 @@ void GLCanvas3D::load_arrange_settings() std::string en_rot_sla_str = wxGetApp().app_config->get("arrange", "enable_rotation_sla"); +// std::string alignment_fff_str = +// wxGetApp().app_config->get("arrange", "alignment_fff"); + +// std::string alignment_fff_seqp_str = +// wxGetApp().app_config->get("arrange", "alignment_fff_seq_pring"); + +// std::string alignment_sla_str = +// wxGetApp().app_config->get("arrange", "alignment_sla"); + + // Override default alignment and save save/load it to a temporary slot "alignment_xl" + std::string alignment_xl_str = + wxGetApp().app_config->get("arrange", "alignment_xl"); + if (!dist_fff_str.empty()) m_arrange_settings_fff.distance = std::stof(dist_fff_str); @@ -948,6 +961,24 @@ void GLCanvas3D::load_arrange_settings() if (!en_rot_sla_str.empty()) m_arrange_settings_sla.enable_rotation = (en_rot_sla_str == "1" || en_rot_sla_str == "yes"); + +// if (!alignment_sla_str.empty()) +// m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str); + +// if (!alignment_fff_str.empty()) +// m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str); + +// if (!alignment_fff_seqp_str.empty()) +// m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str); + + // Override default alignment and save save/load it to a temporary slot "alignment_xl" + int arr_alignment = static_cast(arrangement::Pivots::BottomLeft); + if (!alignment_xl_str.empty()) + arr_alignment = std::stoi(alignment_xl_str); + + m_arrange_settings_sla.alignment = arr_alignment ; + m_arrange_settings_fff.alignment = arr_alignment ; + m_arrange_settings_fff_seq_print.alignment = arr_alignment ; } PrinterTechnology GLCanvas3D::current_printer_technology() const @@ -955,6 +986,20 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const return m_process->current_printer_technology(); } +bool GLCanvas3D::is_arrange_alignment_enabled() const +{ + static constexpr const char *ALIGN_ONLY_FOR = "XL"; + + bool ret = false; + + auto *printer_model = m_config->opt("printer_model"); + + if (printer_model) + ret = boost::algorithm::contains(printer_model->value, ALIGN_ONLY_FOR); + + return ret; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) : m_canvas(canvas) , m_context(nullptr) @@ -4019,14 +4064,16 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) imgui->begin(_L("Arrange options"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); ArrangeSettings settings = get_arrange_settings(); - ArrangeSettings &settings_out = get_arrange_settings(); + ArrangeSettings &settings_out = get_arrange_settings_ref(this); auto &appcfg = wxGetApp().app_config; PrinterTechnology ptech = current_printer_technology(); bool settings_changed = false; float dist_min = 0.f; - std::string dist_key = "min_object_distance", rot_key = "enable_rotation"; + std::string dist_key = "min_object_distance"; + std::string rot_key = "enable_rotation"; + std::string align_key = "alignment"; std::string postfix; if (ptech == ptSLA) { @@ -4044,7 +4091,8 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) } dist_key += postfix; - rot_key += postfix; + rot_key += postfix; + align_key += postfix; imgui->text(GUI::format_wxstr(_L("Press %1%left mouse button to enter the exact value"), shortkey_ctrl_prefix())); @@ -4061,6 +4109,14 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x) settings_changed = true; } + Points bed = m_config ? get_bed_shape(*m_config) : Points{}; + + if (arrangement::is_box(bed) && settings.alignment >= 0 && imgui->combo(_("Alignment"), {"Center", "Top left", "Bottom left", "Bottom right", "Top right", "Random"}, settings.alignment)) { + settings_out.alignment = settings.alignment; + appcfg->set("arrange", align_key.c_str(), std::to_string(settings_out.alignment)); + settings_changed = true; + } + ImGui::Separator(); if (imgui->button(_L("Reset"))) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 605d75ddd..b09496f0f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -444,6 +444,7 @@ public: // float distance_sla = 6.; float accuracy = 0.65f; // Unused currently bool enable_rotation = false; + int alignment = 0; }; private: @@ -525,8 +526,10 @@ private: PrinterTechnology current_printer_technology() const; + bool is_arrange_alignment_enabled() const; + template - static auto & get_arrange_settings(Self *self) { + static auto & get_arrange_settings_ref(Self *self) { PrinterTechnology ptech = self->current_printer_technology(); auto *ptr = &self->m_arrange_settings_fff; @@ -544,8 +547,22 @@ private: return *ptr; } - ArrangeSettings &get_arrange_settings() { return get_arrange_settings(this); } +public: + ArrangeSettings get_arrange_settings() const { + const ArrangeSettings &settings = get_arrange_settings_ref(this); + ArrangeSettings ret = settings; + if (&settings == &m_arrange_settings_fff_seq_print) { + ret.distance = std::max(ret.distance, + float(min_object_distance(*m_config))); + } + if (!is_arrange_alignment_enabled()) + ret.alignment = -1; + + return ret; + } + +private: void load_arrange_settings(); class SequentialPrintClearance @@ -831,17 +848,6 @@ public: void highlight_toolbar_item(const std::string& item_name); void highlight_gizmo(const std::string& gizmo_name); - ArrangeSettings get_arrange_settings() const { - const ArrangeSettings &settings = get_arrange_settings(this); - ArrangeSettings ret = settings; - if (&settings == &m_arrange_settings_fff_seq_print) { - ret.distance = std::max(ret.distance, - float(min_object_distance(*m_config))); - } - - return ret; - } - // Timestamp for FPS calculation and notification fade-outs. static int64_t timestamp_now() { #ifdef _WIN32 diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 2771f9d27..b4c49ab01 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -14,6 +14,9 @@ #include "libnest2d/common.hpp" +#include +#include + namespace Slic3r { namespace GUI { // Cache the wti info @@ -278,12 +281,29 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, arrangement::ArrangeParams get_arrange_params(Plater *p) { const GLCanvas3D::ArrangeSettings &settings = - static_cast(p->canvas3D())->get_arrange_settings(); + p->canvas3D()->get_arrange_settings(); arrangement::ArrangeParams params; params.allow_rotations = settings.enable_rotation; params.min_obj_distance = scaled(settings.distance); + arrangement::Pivots pivot = arrangement::Pivots::Center; + + int pivot_max = static_cast(arrangement::Pivots::TopRight); + if (settings.alignment < 0) { + pivot = arrangement::Pivots::Center; + } else if (settings.alignment > pivot_max) { + // means it should be random + std::random_device rd{}; + std::mt19937 rng(rd()); + std::uniform_int_distribution dist(0, pivot_max); + pivot = static_cast(dist(rng)); + } else { + pivot = static_cast(settings.alignment); + } + + params.alignment = pivot; + return params; } diff --git a/src/slic3r/GUI/Jobs/FillBedJob.cpp b/src/slic3r/GUI/Jobs/FillBedJob.cpp index 870f31f2f..c6c760786 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.cpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.cpp @@ -104,14 +104,10 @@ void FillBedJob::prepare() void FillBedJob::process() { - if (m_object_idx == -1 || m_selected.empty()) return; + if (m_object_idx == -1 || m_selected.empty()) + return; - const GLCanvas3D::ArrangeSettings &settings = - static_cast(m_plater->canvas3D())->get_arrange_settings(); - - arrangement::ArrangeParams params; - params.allow_rotations = settings.enable_rotation; - params.min_obj_distance = scaled(settings.distance); + arrangement::ArrangeParams params = get_arrange_params(m_plater); bool do_stop = false; params.stopcondition = [this, &do_stop]() {