diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 6cdaadd25..7b4f36716 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -479,13 +479,18 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer; - // Norming factor for the optimization function - const double norm_; - public: using Pile = nfp::Shapes; +private: + + // Norming factor for the optimization function + const double norm_; + Pile merged_pile_; + +public: + inline explicit _NofitPolyPlacer(const BinType& bin): Base(bin), norm_(std::sqrt(sl::area(bin))) @@ -576,6 +581,20 @@ private: using Shapes = TMultiShape; + template + static Shapes calcnfp(const Shapes &pile, const RawShape &orb) + { + Shapes ret; ret.reserve(2 * pile.size()); + + for (auto &stat : pile) { + Shapes subnfp = nfp::noFitPolygon(stat, orb); + for (auto &nfp : subnfp) + ret.emplace_back(subnfp); + } + + return nfp::merge(ret); + } + Shapes calcnfp(const Item &trsh, Lvl) { using namespace nfp; @@ -616,135 +635,9 @@ private: template Shapes calcnfp(const Item &trsh, Level) { // Function for arbitrary level of nfp implementation - using namespace nfp; - Shapes nfps; - - auto& orb = trsh.transformedShape(); - bool orbconvex = trsh.isContourConvex(); - - for(Item& sh : items_) { - nfp::NfpResult subnfp; - auto& stat = sh.transformedShape(); - - if(sh.isContourConvex() && orbconvex) - subnfp = nfp::noFitPolygon(stat, orb); - else if(orbconvex) - subnfp = nfp::noFitPolygon(stat, orb); - else - subnfp = nfp::noFitPolygon(stat, orb); - - correctNfpPosition(subnfp, sh, trsh); - - nfps = nfp::merge(nfps, subnfp.first); - } - - return nfps; - } - - // Very much experimental - void repack(Item& item, PackResult& result) { - - if((sl::area(bin_) - this->filledArea()) >= item.area()) { - auto prev_func = config_.object_function; - - unsigned iter = 0; - ItemGroup backup_rf = items_; - std::vector backup_cpy; - for(Item& itm : items_) backup_cpy.emplace_back(itm); - - auto ofn = [this, &item, &result, &iter, &backup_cpy, &backup_rf] - (double ratio) - { - auto& bin = bin_; - iter++; - config_.object_function = [bin, ratio]( - nfp::Shapes& pile, - const Item& item, - const ItemGroup& /*remaining*/) - { - pile.emplace_back(item.transformedShape()); - auto ch = sl::convexHull(pile); - auto pbb = sl::boundingBox(pile); - pile.pop_back(); - - double parea = 0.5*(sl::area(ch) + sl::area(pbb)); - - double pile_area = std::accumulate( - pile.begin(), pile.end(), item.area(), - [](double sum, const RawShape& sh){ - return sum + sl::area(sh); - }); - - // The pack ratio -- how much is the convex hull occupied - double pack_rate = (pile_area)/parea; - - // ratio of waste - double waste = 1.0 - pack_rate; - - // Score is the square root of waste. This will extend the - // range of good (lower) values and shrink the range of bad - // (larger) values. - auto wscore = std::sqrt(waste); - - - auto ibb = item.boundingBox(); - auto bbb = sl::boundingBox(bin); - auto c = ibb.center(); - double norm = 0.5*pl::distance(bbb.minCorner(), - bbb.maxCorner()); - - double dscore = pl::distance(c, pbb.center()) / norm; - - return ratio*wscore + (1.0 - ratio) * dscore; - }; - - auto bb = sl::boundingBox(bin); - double norm = bb.width() + bb.height(); - - auto items = items_; - clearItems(); - auto it = items.begin(); - while(auto pr = _trypack(*it++)) { - this->accept(pr); if(it == items.end()) break; - } - - auto count_diff = items.size() - items_.size(); - double score = count_diff; - - if(count_diff == 0) { - result = _trypack(item); - - if(result) { - std::cout << "Success" << std::endl; - score = 0.0; - } else { - score += result.overfit() / norm; - } - } else { - result = PackResult(); - items_ = backup_rf; - for(unsigned i = 0; i < items_.size(); i++) { - items_[i].get() = backup_cpy[i]; - } - } - - std::cout << iter << " repack result: " << score << " " - << ratio << " " << count_diff << std::endl; - - return score; - }; - - opt::StopCriteria stopcr; - stopcr.max_iterations = 30; - stopcr.stop_score = 1e-20; - opt::TOptimizer solver(stopcr); - solver.optimize_min(ofn, opt::initvals(0.5), - opt::bound(0.0, 1.0)); - - // optimize - config_.object_function = prev_func; - } + // TODO: implement + return {}; } struct Optimum { @@ -798,6 +691,50 @@ private: Radians final_rot = initial_rot; Shapes nfps; + auto& bin = bin_; + double norm = norm_; + auto pbb = sl::boundingBox(merged_pile_); + auto binbb = sl::boundingBox(bin); + + // This is the kernel part of the object function that is + // customizable by the library client + std::function _objfunc; + if(config_.object_function) _objfunc = config_.object_function; + else { + + // Inside check has to be strict if no alignment was enabled + std::function ins_check; + if(config_.alignment == Config::Alignment::DONT_ALIGN) + ins_check = [&binbb, norm](const Box& fullbb) { + double ret = 0; + if(!sl::isInside(fullbb, binbb)) + ret += norm; + return ret; + }; + else + ins_check = [&bin](const Box& fullbb) { + double miss = overfit(fullbb, bin); + miss = miss > 0? miss : 0; + return std::pow(miss, 2); + }; + + _objfunc = [norm, binbb, pbb, ins_check](const Item& item) + { + auto ibb = item.boundingBox(); + auto fullbb = sl::boundingBox(pbb, ibb); + + double score = pl::distance(ibb.center(), + binbb.center()); + score /= norm; + + score += ins_check(fullbb); + + return score; + }; + } + + Pile merged_pile = merged_pile_; + for(auto rot : config_.rotations) { item.translation(initial_tr); @@ -822,57 +759,6 @@ private: ecache.back().accuracy(config_.accuracy); } - Shapes pile; - pile.reserve(items_.size()+1); - // double pile_area = 0; - for(Item& mitem : items_) { - pile.emplace_back(mitem.transformedShape()); - // pile_area += mitem.area(); - } - - auto merged_pile = nfp::merge(pile); - auto& bin = bin_; - double norm = norm_; - auto pbb = sl::boundingBox(merged_pile); - auto binbb = sl::boundingBox(bin); - - // This is the kernel part of the object function that is - // customizable by the library client - std::function _objfunc; - if(config_.object_function) _objfunc = config_.object_function; - else { - - // Inside check has to be strict if no alignment was enabled - std::function ins_check; - if(config_.alignment == Config::Alignment::DONT_ALIGN) - ins_check = [&binbb, norm](const Box& fullbb) { - double ret = 0; - if(!sl::isInside(fullbb, binbb)) - ret += norm; - return ret; - }; - else - ins_check = [&bin](const Box& fullbb) { - double miss = overfit(fullbb, bin); - miss = miss > 0? miss : 0; - return std::pow(miss, 2); - }; - - _objfunc = [norm, binbb, pbb, ins_check](const Item& item) - { - auto ibb = item.boundingBox(); - auto fullbb = sl::boundingBox(pbb, ibb); - - double score = pl::distance(ibb.center(), - binbb.center()); - score /= norm; - - score += ins_check(fullbb); - - return score; - }; - } - // Our object function for placement auto rawobjfunc = [_objfunc, iv, startpos] (Vertex v, Item& itm) @@ -1041,6 +927,7 @@ private: item.translation(final_tr); item.rotation(final_rot); + merged_pile_ = nfp::merge(merged_pile, item.transformedShape()); } if(can_pack) {