diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index 4d1e62f99..8841d1b73 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -640,6 +640,7 @@ public: // The progress function will be called with the number of placed items using ProgressFunction = std::function; +using StopCondition = std::function; /** * A wrapper interface (trait) class for any selections strategy provider. @@ -674,6 +675,8 @@ public: */ void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); } + void stopCondition(StopCondition cond) { impl_.stopCondition(cond); } + /** * \brief A method to start the calculation on the input sequence. * @@ -864,6 +867,11 @@ public: selector_.progressIndicator(func); return *this; } + /// Set a predicate to tell when to abort nesting. + inline Nester& stopCondition(StopCondition fn) { + selector_.stopCondition(fn); return *this; + } + inline PackGroup lastResult() { PackGroup ret; for(size_t i = 0; i < selector_.binCount(); i++) { diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp index ee93d0592..39761f557 100644 --- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp +++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp @@ -551,7 +551,7 @@ public: // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the not_packed list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !this->stopcond_()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it, rem(it, store_))) { it = store_.erase(it); @@ -592,9 +592,11 @@ public: bool do_pairs = config_.try_pairs; bool do_triplets = config_.try_triplets; + StopCondition stopcond = this->stopcond_; // The DJD heuristic algorithm itself: auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs, + stopcond, &tryOneByOne, &tryGroupsOfTwo, &tryGroupsOfThree, @@ -606,12 +608,12 @@ public: double waste = .0; bool lasttry = false; - while(!not_packed.empty()) { + while(!not_packed.empty() && !stopcond()) { {// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity auto it = not_packed.begin(); - while(it != not_packed.end() && + while(it != not_packed.end() && !stopcond() && filled_area < INITIAL_FILL_AREA) { if(placer.pack(*it, rem(it, not_packed))) { @@ -623,14 +625,14 @@ public: } } - // try pieses one by one + // try pieces one by one while(tryOneByOne(placer, not_packed, waste, free_area, filled_area)) { waste = 0; lasttry = false; makeProgress(placer, idx, 1); } - // try groups of 2 pieses + // try groups of 2 pieces while(do_pairs && tryGroupsOfTwo(placer, not_packed, waste, free_area, filled_area)) { @@ -638,7 +640,7 @@ public: makeProgress(placer, idx, 2); } - // try groups of 3 pieses + // try groups of 3 pieces while(do_triplets && tryGroupsOfThree(placer, not_packed, waste, free_area, filled_area)) { diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp index 0da7220a1..5f95a6eff 100644 --- a/xs/src/libnest2d/libnest2d/selections/filler.hpp +++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp @@ -60,7 +60,7 @@ public: placer.configure(pconfig); auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !this->stopcond_()) { if(!placer.pack(*it, {std::next(it), store_.end()})) { if(packed_bins_.back().empty()) ++it; placer.clearItems(); diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp index bca7497db..6bb9c60e4 100644 --- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp +++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp @@ -56,10 +56,12 @@ public: this->progress_(static_cast(--total)); }; + auto& cancelled = this->stopcond_; + // Safety test: try to pack each item into an empty bin. If it fails // then it should be removed from the list { auto it = store_.begin(); - while (it != store_.end()) { + while (it != store_.end() && !cancelled()) { Placer p(bin); p.configure(pconfig); if(!p.pack(*it)) { it = store_.erase(it); @@ -67,13 +69,14 @@ public: } } + auto it = store_.begin(); - while(it != store_.end()) { + while(it != store_.end() && !cancelled()) { bool was_packed = false; size_t j = 0; - while(!was_packed) { - for(; j < placers.size() && !was_packed; j++) { + while(!was_packed && !cancelled()) { + for(; j < placers.size() && !was_packed && !cancelled(); j++) { if((was_packed = placers[j].pack(*it, rem(it, store_) ))) makeProgress(placers[j], j); } diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp index 05bbae658..cfb98a9c8 100644 --- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp +++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp @@ -2,6 +2,7 @@ #define SELECTION_BOILERPLATE_HPP #include "../libnest2d.hpp" +#include namespace libnest2d { namespace selections { @@ -25,14 +26,15 @@ public: return packed_bins_[binIndex]; } - inline void progressIndicator(ProgressFunction fn) { - progress_ = fn; - } + inline void progressIndicator(ProgressFunction fn) { progress_ = fn; } + + inline void stopCondition(StopCondition cond) { stopcond_ = cond; } protected: PackGroup packed_bins_; ProgressFunction progress_ = [](unsigned){}; + StopCondition stopcond_ = [](){ return false; }; }; } diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index 36bd8ed97..a8e7936d6 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -299,7 +299,8 @@ protected: public: _ArrBase(const TBin& bin, Distance dist, - std::function progressind): + std::function progressind, + std::function stopcond): pck_(bin, dist), bin_area_(sl::area(bin)), norm_(std::sqrt(sl::area(bin))) { @@ -330,6 +331,7 @@ public: }; pck_.progressIndicator(progressind); + pck_.stopCondition(stopcond); } template inline IndexedPackGroup operator()(Args&&...args) { @@ -343,8 +345,9 @@ class AutoArranger: public _ArrBase { public: AutoArranger(const Box& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, bin] (const Item &item) { @@ -380,8 +383,9 @@ class AutoArranger: public _ArrBase { public: AutoArranger(const lnCircle& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) { + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, &bin] (const Item &item) { @@ -421,8 +425,9 @@ template<> class AutoArranger: public _ArrBase { public: AutoArranger(const PolygonImpl& bin, Distance dist, - std::function progressind): - _ArrBase(bin, dist, progressind) + std::function progressind, + std::function stopcond): + _ArrBase(bin, dist, progressind, stopcond) { pconf_.object_function = [this, &bin] (const Item &item) { @@ -449,8 +454,9 @@ template<> // Specialization with no bin class AutoArranger: public _ArrBase { public: - AutoArranger(Distance dist, std::function progressind): - _ArrBase(Box(0, 0), dist, progressind) + AutoArranger(Distance dist, std::function progressind, + std::function stopcond): + _ArrBase(Box(0, 0), dist, progressind, stopcond) { this->pconf_.object_function = [this] (const Item &item) { @@ -680,12 +686,16 @@ void applyResult( * remaining items which do not fit onto the print area next to the print * bed or leave them untouched (let the user arrange them by hand or remove * them). + * \param progressind Progress indicator callback called when an object gets + * packed. The unsigned argument is the number of items remaining to pack. + * \param stopcondition A predicate returning true if abort is needed. */ bool arrange(Model &model, coordf_t min_obj_distance, const Slic3r::Polyline& bed, BedShapeHint bedhint, bool first_bin_only, - std::function progressind) + std::function progressind, + std::function stopcondition) { using ArrangeResult = _IndexedPackGroup; @@ -710,6 +720,8 @@ bool arrange(Model &model, coordf_t min_obj_distance, BoundingBox bbb(bed); + auto& cfn = stopcondition; + auto binbb = Box({ static_cast(bbb.min(0)), static_cast(bbb.min(1)) @@ -723,7 +735,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, case BedShapeType::BOX: { // Create the arranger for the box shaped bed - AutoArranger arrange(binbb, min_obj_distance, progressind); + AutoArranger arrange(binbb, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -735,7 +747,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto c = bedhint.shape.circ; auto cc = lnCircle(c); - AutoArranger arrange(cc, min_obj_distance, progressind); + AutoArranger arrange(cc, min_obj_distance, progressind, cfn); result = arrange(shapes.begin(), shapes.end()); break; } @@ -747,7 +759,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); P irrbed = sl::create(std::move(ctour)); - AutoArranger

arrange(irrbed, min_obj_distance, progressind); + AutoArranger

arrange(irrbed, min_obj_distance, progressind, cfn); // Arrange and return the items with their respective indices within the // input sequence. @@ -756,7 +768,7 @@ bool arrange(Model &model, coordf_t min_obj_distance, } }; - if(result.empty()) return false; + if(result.empty() || stopcondition()) return false; if(first_bin_only) { applyResult(result.front(), 0, shapemap); diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 2cbf90d31..2c34a4749 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -438,6 +438,11 @@ void AppController::arrange_model() { using Coord = libnest2d::TCoord; + if(arranging_.load()) return; + + // to prevent UI reentrancies + arranging_.store(true); + unsigned count = 0; for(auto obj : model_->objects) count += obj->instances.size(); @@ -451,8 +456,8 @@ void AppController::arrange_model() // Set the range of the progress to the object count pind->max(count); - pind->on_cancel([](){ - std::cout << "Cannot be cancelled!" << std::endl; + pind->on_cancel([this](){ + arranging_.store(false); }); } @@ -478,10 +483,12 @@ void AppController::arrange_model() bed, hint, false, // create many piles not just one pile - [pind, count](unsigned rem) { + [this, pind, count](unsigned rem) { if(pind) - pind->update(count - rem, _(L("Arranging objects..."))); - }); + pind->update(count - rem, L("Arranging objects...")); + + process_events(); + }, [this] () { return !arranging_.load(); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl; report_issue(IssueType::ERR, @@ -493,9 +500,13 @@ void AppController::arrange_model() // Restore previous max value if(pind) { pind->max(pmax); - pind->update(0, _(L("Arranging done."))); + pind->update(0, arranging_.load() ? L("Arranging done.") : + L("Arranging canceled.")); + pind->on_cancel(/*remove cancel function*/); } + + arranging_.store(false); } } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index f24444359..5a7294f79 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -237,6 +237,7 @@ public: class AppController: public AppControllerBoilerplate { Model *model_ = nullptr; PrintController::Ptr printctl; + std::atomic arranging_; public: /** diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index 39192e7d3..279b6d787 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -28,7 +28,7 @@ bool AppControllerBoilerplate::supports_asynch() const void AppControllerBoilerplate::process_events() { - wxSafeYield(); + wxYieldIfNeeded(); } AppControllerBoilerplate::PathList