diff options
Diffstat (limited to 'src/slic3r/GUI/Jobs/ArrangeJob.cpp')
-rw-r--r-- | src/slic3r/GUI/Jobs/ArrangeJob.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp new file mode 100644 index 000000000..0f17e6e9f --- /dev/null +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -0,0 +1,227 @@ +#include "ArrangeJob.hpp" + +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/Model.hpp" + +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" + +namespace Slic3r { namespace GUI { + +// Cache the wti info +class WipeTower: public GLCanvas3D::WipeTowerInfo { + using ArrangePolygon = arrangement::ArrangePolygon; +public: + explicit WipeTower(const GLCanvas3D::WipeTowerInfo &wti) + : GLCanvas3D::WipeTowerInfo(wti) + {} + + explicit WipeTower(GLCanvas3D::WipeTowerInfo &&wti) + : GLCanvas3D::WipeTowerInfo(std::move(wti)) + {} + + void apply_arrange_result(const Vec2d& tr, double rotation) + { + m_pos = unscaled(tr); m_rotation = rotation; + apply_wipe_tower(); + } + + ArrangePolygon get_arrange_polygon() const + { + Polygon ap({ + {scaled(m_bb.min)}, + {scaled(m_bb.max.x()), scaled(m_bb.min.y())}, + {scaled(m_bb.max)}, + {scaled(m_bb.min.x()), scaled(m_bb.max.y())} + }); + + ArrangePolygon ret; + ret.poly.contour = std::move(ap); + ret.translation = scaled(m_pos); + ret.rotation = m_rotation; + ++ret.priority; + + return ret; + } +}; + +static WipeTower get_wipe_tower(const Plater &plater) +{ + return WipeTower{plater.canvas3D()->get_wipe_tower_info()}; +} + +void ArrangeJob::clear_input() +{ + const Model &model = m_plater->model(); + + size_t count = 0, cunprint = 0; // To know how much space to reserve + for (auto obj : model.objects) + for (auto mi : obj->instances) + mi->printable ? count++ : cunprint++; + + m_selected.clear(); + m_unselected.clear(); + m_unprintable.clear(); + m_selected.reserve(count + 1 /* for optional wti */); + m_unselected.reserve(count + 1 /* for optional wti */); + m_unprintable.reserve(cunprint /* for optional wti */); +} + +void ArrangeJob::prepare_all() { + clear_input(); + + for (ModelObject *obj: m_plater->model().objects) + for (ModelInstance *mi : obj->instances) { + ArrangePolygons & cont = mi->printable ? m_selected : m_unprintable; + cont.emplace_back(get_arrange_poly(PtrWrapper{mi}, m_plater)); + } + + if (auto wti = get_wipe_tower_arrangepoly(*m_plater)) + m_selected.emplace_back(std::move(*wti)); +} + +void ArrangeJob::prepare_selected() { + clear_input(); + + Model &model = m_plater->model(); + double stride = bed_stride(m_plater); + + std::vector<const Selection::InstanceIdxsList *> + obj_sel(model.objects.size(), nullptr); + + for (auto &s : m_plater->get_selection().get_content()) + if (s.first < int(obj_sel.size())) + obj_sel[size_t(s.first)] = &s.second; + + // Go through the objects and check if inside the selection + for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { + const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; + ModelObject *mo = model.objects[oidx]; + + std::vector<bool> inst_sel(mo->instances.size(), false); + + if (instlist) + for (auto inst_id : *instlist) + inst_sel[size_t(inst_id)] = true; + + for (size_t i = 0; i < inst_sel.size(); ++i) { + ArrangePolygon &&ap = + get_arrange_poly(PtrWrapper{mo->instances[i]}, m_plater); + + ArrangePolygons &cont = mo->instances[i]->printable ? + (inst_sel[i] ? m_selected : + m_unselected) : + m_unprintable; + + cont.emplace_back(std::move(ap)); + } + } + + if (auto wti = get_wipe_tower(*m_plater)) { + ArrangePolygon &&ap = get_arrange_poly(wti, m_plater); + + auto &cont = m_plater->get_selection().is_wipe_tower() ? m_selected : + m_unselected; + cont.emplace_back(std::move(ap)); + } + + // 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; +} + +void ArrangeJob::prepare() +{ + wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); +} + +void ArrangeJob::process() +{ + static const auto arrangestr = _(L("Arranging")); + + const GLCanvas3D::ArrangeSettings &settings = + static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings(); + + arrangement::ArrangeParams params; + params.allow_rotations = settings.enable_rotation; + params.min_obj_distance = scaled(settings.distance); + + + auto count = unsigned(m_selected.size() + m_unprintable.size()); + Points bedpts = get_bed_shape(*m_plater->config()); + + params.stopcondition = [this]() { return was_canceled(); }; + + try { + params.progressind = [this, count](unsigned st) { + st += m_unprintable.size(); + if (st > 0) update_status(int(count - st), arrangestr); + }; + + arrangement::arrange(m_selected, m_unselected, bedpts, params); + + params.progressind = [this, count](unsigned st) { + if (st > 0) update_status(int(count - st), arrangestr); + }; + + arrangement::arrange(m_unprintable, {}, bedpts, params); + } catch (std::exception & /*e*/) { + GUI::show_error(m_plater, + _(L("Could not arrange model objects! " + "Some geometries may be invalid."))); + } + + // finalize just here. + update_status(int(count), + was_canceled() ? _(L("Arranging canceled.")) + : _(L("Arranging done."))); +} + +void ArrangeJob::finalize() { + // Ignore the arrange result if aborted. + if (was_canceled()) return; + + // Unprintable items go to the last virtual bed + int beds = 0; + + // Apply the arrange result to all selected objects + for (ArrangePolygon &ap : m_selected) { + beds = std::max(ap.bed_idx, beds); + ap.apply(); + } + + // Get the virtual beds from the unselected items + for (ArrangePolygon &ap : m_unselected) + beds = std::max(ap.bed_idx, beds); + + // Move the unprintable items to the last virtual bed. + for (ArrangePolygon &ap : m_unprintable) { + ap.bed_idx += beds + 1; + ap.apply(); + } + + m_plater->update(); + + Job::finalize(); +} + +std::optional<arrangement::ArrangePolygon> +get_wipe_tower_arrangepoly(const Plater &plater) +{ + if (auto wti = get_wipe_tower(plater)) + return get_arrange_poly(wti, &plater); + + return {}; +} + +double bed_stride(const Plater *plater) { + double bedwidth = plater->bed_shape_bb().size().x(); + return scaled<double>((1. + LOGICAL_BED_GAP) * bedwidth); +} + +}} // namespace Slic3r::GUI |