From 5154a7f8b3b62297d4db6e79ee444034bc9e92cb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 5 Jun 2023 15:04:27 +0200 Subject: [PATCH] Fix arrange on virtual beds Also fixes repeated bed filling issue --- src/libslic3r/Arrange.cpp | 16 +++++++++++ src/libslic3r/Arrange.hpp | 19 +++++++++++++ src/slic3r/GUI/Jobs/ArrangeJob.cpp | 45 +++++++++++++++++++++--------- src/slic3r/GUI/Jobs/ArrangeJob.hpp | 6 +++- src/slic3r/GUI/Jobs/FillBedJob.cpp | 34 +++++++++++----------- src/slic3r/GUI/Jobs/FillBedJob.hpp | 2 +- 6 files changed, 89 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 6042c6ded..ea71a1a55 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -755,5 +755,21 @@ void arrange(ArrangePolygons &items, } } +BoundingBox bounding_box(const InfiniteBed &bed) +{ + BoundingBox ret; + using C = coord_t; + + // It is important for Mx and My to be strictly less than half of the + // range of type C. width(), height() and area() will not overflow this way. + C Mx = C((std::numeric_limits::lowest() + 2 * bed.center.x()) / 4.01); + C My = C((std::numeric_limits::lowest() + 2 * bed.center.y()) / 4.01); + + ret.max = bed.center - Point{Mx, My}; + ret.min = bed.center + Point{Mx, My}; + + return ret; +} + } // namespace arr } // namespace Slic3r diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 874742ecb..f705a1ad8 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -55,6 +55,25 @@ struct IrregularBed { using ArrangeBed = boost::variant; +BoundingBox bounding_box(const InfiniteBed &bed); +inline BoundingBox bounding_box(const RectangleBed &b) { return b.bb; } +inline BoundingBox bounding_box(const SegmentedRectangleBed &b) { return b.bb; } +inline BoundingBox bounding_box(const CircleBed &b) +{ + auto r = static_cast(std::round(b.radius())); + Point R{r, r}; + + return {b.center() - R, b.center() + R}; +} +inline BoundingBox bounding_box(const ArrangeBed &b) +{ + BoundingBox ret; + auto visitor = [&ret](const auto &b) { ret = bounding_box(b); }; + boost::apply_visitor(visitor, b); + + return ret; +} + ArrangeBed to_arrange_bed(const Points &bedpts); /// A logical bed representing an object not being arranged. Either the arrange diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 47cd01fdb..b5c328ae8 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -99,7 +99,6 @@ void ArrangeJob::prepare_selected() { clear_input(); Model &model = m_plater->model(); - double stride = bed_stride(m_plater); std::vector obj_sel(model.objects.size(), nullptr); @@ -143,12 +142,6 @@ void ArrangeJob::prepare_selected() { // If the selection was empty arrange everything if (m_selected.empty()) m_selected.swap(m_unselected); - - // The strides have to be removed from the fixed items. For the - // arrangeable (selected) items bed_idx is ignored and the - // translation is irrelevant. - for (auto &p : m_unselected) - p.translation(X) -= p.bed_idx * stride; } static void update_arrangepoly_slaprint(arrangement::ArrangePolygon &ret, @@ -261,6 +254,10 @@ void ArrangeJob::prepare() } m_min_bed_inset = min_offset; } + + double stride = bed_stride(m_plater); + get_bed_shape(*m_plater->config(), m_bed); + assign_logical_beds(m_unselected, m_bed, stride); } void ArrangeJob::process(Ctl &ctl) @@ -268,11 +265,9 @@ void ArrangeJob::process(Ctl &ctl) static const auto arrangestr = _u8L("Arranging"); arrangement::ArrangeParams params; - arrangement::ArrangeBed bed; - ctl.call_on_main_thread([this, ¶ms, &bed]{ + ctl.call_on_main_thread([this, ¶ms]{ prepare(); params = get_arrange_params(m_plater); - get_bed_shape(*m_plater->config(), bed); coord_t min_inset = get_skirt_offset(m_plater) + m_min_bed_inset; params.min_bed_distance = std::max(params.min_bed_distance, min_inset); }).wait(); @@ -293,13 +288,13 @@ void ArrangeJob::process(Ctl &ctl) ctl.update_status(0, arrangestr); - arrangement::arrange(m_selected, m_unselected, bed, params); + arrangement::arrange(m_selected, m_unselected, m_bed, params); params.progressind = [this, count, &ctl](unsigned st) { if (st > 0) ctl.update_status(int(count - st) * 100 / status_range(), arrangestr); }; - arrangement::arrange(m_unprintable, {}, bed, params); + arrangement::arrange(m_unprintable, {}, m_bed, params); // finalize just here. ctl.update_status(int(count) * 100 / status_range(), ctl.was_canceled() ? @@ -358,7 +353,9 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) { ap.apply(); } - m_plater->update((unsigned int)Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH); + m_plater->update(static_cast( + Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + wxGetApp().obj_manipul()->set_dirty(); if (!m_unarranged.empty()) { @@ -442,4 +439,26 @@ arrangement::ArrangeParams get_arrange_params(Plater *p) return params; } +void assign_logical_beds(std::vector &items, + const arrangement::ArrangeBed &bed, + double stride) +{ + + // The strides have to be removed from the fixed items. For the + // arrangeable (selected) items bed_idx is ignored and the + // translation is irrelevant. + coord_t bedx = bounding_box(bed).min.x(); + for (auto &itm : items) { + auto bedidx = std::max(arrangement::UNARRANGED, + static_cast(std::floor( + (get_extents(itm.transformed_poly()).min.x() - bedx) / + stride))); + + itm.bed_idx = bedidx; + + if (bedidx >= 0) + itm.translation.x() -= bedidx * stride; + } +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp index 4defc07e8..c7070f213 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.hpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp @@ -21,6 +21,7 @@ class ArrangeJob : public Job ArrangePolygons m_selected, m_unselected, m_unprintable; std::vector m_unarranged; + arrangement::ArrangeBed m_bed; coord_t m_min_bed_inset = 0.; Plater *m_plater; @@ -89,7 +90,6 @@ arrangement::ArrangePolygon get_arrange_poly(T obj, const Plater *plater) using ArrangePolygon = arrangement::ArrangePolygon; ArrangePolygon ap = obj.get_arrange_polygon(); - ap.bed_idx = get_extents(ap.transformed_poly()).min.x() / bed_stride(plater); ap.setter = [obj, plater](const ArrangePolygon &p) { if (p.is_arranged()) { Vec2d t = p.translation.cast(); @@ -109,6 +109,10 @@ arrangement::ArrangeParams get_arrange_params(Plater *p); coord_t get_skirt_offset(const Plater* plater); +void assign_logical_beds(std::vector &items, + const arrangement::ArrangeBed &bed, + double stride); + }} // namespace Slic3r::GUI #endif // ARRANGEJOB_HPP diff --git a/src/slic3r/GUI/Jobs/FillBedJob.cpp b/src/slic3r/GUI/Jobs/FillBedJob.cpp index 11d9450c2..7594d3210 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.cpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.cpp @@ -17,7 +17,6 @@ void FillBedJob::prepare() { m_selected.clear(); m_unselected.clear(); - m_bedpts.clear(); m_min_bed_inset = 0.; m_object_idx = m_plater->get_selected_object_idx(); @@ -41,10 +40,10 @@ void FillBedJob::prepare() if (m_selected.empty()) return; - m_bedpts = get_bed_shape(*m_plater->config()); + Points bedpts = get_bed_shape(*m_plater->config()); auto &objects = m_plater->model().objects; - BoundingBox bedbb = get_extents(m_bedpts); + BoundingBox bedbb = get_extents(bedpts); for (size_t idx = 0; idx < objects.size(); ++idx) if (int(idx) != m_object_idx) @@ -72,7 +71,7 @@ void FillBedJob::prepare() }) / sc; double fixed_area = unsel_area + m_selected.size() * poly_area; - double bed_area = Polygon{m_bedpts}.area() / sc; + double bed_area = Polygon{bedpts}.area() / sc; // This is the maximum number of items, the real number will always be close but less. int needed_items = (bed_area - fixed_area) / poly_area; @@ -85,13 +84,11 @@ void FillBedJob::prepare() for (int i = 0; i < needed_items; ++i) { ArrangePolygon ap = template_ap; - ap.poly = m_selected.front().poly; ap.bed_idx = arrangement::UNARRANGED; auto m = mi->get_transformation(); - ap.setter = [this, mi, m](const ArrangePolygon &p) { + ap.setter = [this, m](const ArrangePolygon &p) { ModelObject *mo = m_plater->model().objects[m_object_idx]; - ModelInstance *inst = mo->add_instance(*mi); - inst->set_transformation(m); + ModelInstance *inst = mo->add_instance(m); inst->apply_arrange_result(p.translation.cast(), p.rotation); }; m_selected.emplace_back(ap); @@ -99,14 +96,6 @@ void FillBedJob::prepare() m_status_range = m_selected.size(); - // The strides have to be removed from the fixed items. For the - // arrangeable (selected) items bed_idx is ignored and the - // translation is irrelevant. - double stride = bed_stride(m_plater); - for (auto &p : m_unselected) - if (p.bed_idx > 0) - p.translation(X) -= p.bed_idx * stride; - coord_t min_offset = 0; for (auto &ap : m_selected) { min_offset = std::max(ap.inflation, min_offset); @@ -123,6 +112,14 @@ void FillBedJob::prepare() } m_min_bed_inset = min_offset; } + + // The strides have to be removed from the fixed items. For the + // arrangeable (selected) items bed_idx is ignored and the + // translation is irrelevant. + double stride = bed_stride(m_plater); + + m_bed = arrangement::to_arrange_bed(bedpts); + assign_logical_beds(m_unselected, m_bed, stride); } void FillBedJob::process(Ctl &ctl) @@ -154,7 +151,7 @@ void FillBedJob::process(Ctl &ctl) do_stop = ap.bed_idx > 0 && ap.priority == 0; }; - arrangement::arrange(m_selected, m_unselected, m_bedpts, params); + arrangement::arrange(m_selected, m_unselected, m_bed, params); // finalize just here. ctl.update_status(100, ctl.was_canceled() ? @@ -191,7 +188,8 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr) model_object->ensure_on_bed(); - m_plater->update(); + m_plater->update(static_cast( + Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH)); // FIXME: somebody explain why this is needed for increase_object_instances if (inst_cnt == 1) diff --git a/src/slic3r/GUI/Jobs/FillBedJob.hpp b/src/slic3r/GUI/Jobs/FillBedJob.hpp index b953e0e98..ae8680de1 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.hpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.hpp @@ -18,7 +18,7 @@ class FillBedJob : public Job ArrangePolygons m_unselected; coord_t m_min_bed_inset = 0.; - Points m_bedpts; + arrangement::ArrangeBed m_bed; int m_status_range = 0; Plater *m_plater;