diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 6c55d0e3f..345252f12 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -875,6 +875,28 @@ inline _Box> boundingBox(const S& sh) return boundingBox(sh, Tag() ); } +template _Box

boundingBox(const _Box

& bb1, const _Box

& bb2 ) +{ + auto& pminc = bb1.minCorner(); + auto& pmaxc = bb1.maxCorner(); + auto& iminc = bb2.minCorner(); + auto& imaxc = bb2.maxCorner(); + P minc, maxc; + + setX(minc, std::min(getX(pminc), getX(iminc))); + setY(minc, std::min(getY(pminc), getY(iminc))); + + setX(maxc, std::max(getX(pmaxc), getX(imaxc))); + setY(maxc, std::max(getY(pmaxc), getY(imaxc))); + return _Box

(minc, maxc); +} + +template +_Box> boundingBox(const S1 &s1, const S2 &s2) +{ + return boundingBox(boundingBox(s1), boundingBox(s2)); +} + template inline double area(const Box& box, const BoxTag& ) { @@ -1060,8 +1082,8 @@ template inline bool isInside(const TB& box, const TC& circ, const BoxTag&, const CircleTag&) { - return isInside(box.minCorner(), circ, BoxTag(), CircleTag()) && - isInside(box.maxCorner(), circ, BoxTag(), CircleTag()); + return isInside(box.minCorner(), circ, PointTag(), CircleTag()) && + isInside(box.maxCorner(), circ, PointTag(), CircleTag()); } template diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index c7d515d49..ab018f3f8 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -895,7 +895,10 @@ private: template inline void __execute(TIter from, TIter to) { if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { - item.addOffset(static_cast(std::ceil(min_obj_distance_/2.0))); + auto offs = min_obj_distance_; + if (item.isFixed()) offs *= 0.99; + + item.addOffset(static_cast(std::ceil(offs/2.0))); }); selector_.template packItems( diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index c1f15fe61..b94443bff 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -801,7 +801,6 @@ private: // optimize config_.object_function = prev_func; } - } struct Optimum { @@ -816,29 +815,14 @@ private: class Optimizer: public opt::TOptimizer { public: - Optimizer() { + Optimizer(float accuracy = 1.f) { opt::StopCriteria stopcr; - stopcr.max_iterations = 200; + stopcr.max_iterations = unsigned(std::floor(1000 * accuracy)); stopcr.relative_score_difference = 1e-20; this->stopcr_ = stopcr; } }; - static Box boundingBox(const Box& pilebb, const Box& ibb ) { - auto& pminc = pilebb.minCorner(); - auto& pmaxc = pilebb.maxCorner(); - auto& iminc = ibb.minCorner(); - auto& imaxc = ibb.maxCorner(); - Vertex minc, maxc; - - setX(minc, std::min(getX(pminc), getX(iminc))); - setY(minc, std::min(getY(pminc), getY(iminc))); - - setX(maxc, std::max(getX(pmaxc), getX(imaxc))); - setY(maxc, std::max(getY(pmaxc), getY(imaxc))); - return Box(minc, maxc); - } - using Edges = EdgeCache; template> @@ -935,7 +919,7 @@ private: _objfunc = [norm, binbb, pbb, ins_check](const Item& item) { auto ibb = item.boundingBox(); - auto fullbb = boundingBox(pbb, ibb); + auto fullbb = sl::boundingBox(pbb, ibb); double score = pl::distance(ibb.center(), binbb.center()); @@ -1005,14 +989,15 @@ private: auto& rofn = rawobjfunc; auto& nfpoint = getNfpPoint; + float accuracy = config_.accuracy; __parallel::enumerate( cache.corners().begin(), cache.corners().end(), - [&results, &item, &rofn, &nfpoint, ch] + [&results, &item, &rofn, &nfpoint, ch, accuracy] (double pos, size_t n) { - Optimizer solver; + Optimizer solver(accuracy); Item itemcpy = item; auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy] @@ -1059,10 +1044,10 @@ private: __parallel::enumerate(cache.corners(hidx).begin(), cache.corners(hidx).end(), [&results, &item, &nfpoint, - &rofn, ch, hidx] + &rofn, ch, hidx, accuracy] (double pos, size_t n) { - Optimizer solver; + Optimizer solver(accuracy); Item itmcpy = item; auto hole_ofn = diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index debd29024..2c4417b4d 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -65,89 +65,6 @@ using Segment = _Segment; using MultiPolygon = TMultiShape; using PackGroup = _PackGroup; -// Only for debugging. Prints the model object vertices on stdout. -//std::string toString(const Model& model, bool holes = true) { -// std::stringstream ss; - -// ss << "{\n"; - -// for(auto objptr : model.objects) { -// if(!objptr) continue; - -// auto rmesh = objptr->raw_mesh(); - -// for(auto objinst : objptr->instances) { -// if(!objinst) continue; - -// Slic3r::TriangleMesh tmpmesh = rmesh; -// // CHECK_ME -> Is the following correct ? -// tmpmesh.scale(objinst->get_scaling_factor()); -// objinst->transform_mesh(&tmpmesh); -// ExPolygons expolys = tmpmesh.horizontal_projection(); -// for(auto& expoly_complex : expolys) { - -// ExPolygons tmp = expoly_complex.simplify(scaled(1.)); -// if(tmp.empty()) continue; -// ExPolygon expoly = tmp.front(); -// expoly.contour.make_clockwise(); -// for(auto& h : expoly.holes) h.make_counter_clockwise(); - -// ss << "\t{\n"; -// ss << "\t\t{\n"; - -// for(auto v : expoly.contour.points) ss << "\t\t\t{" -// << v(0) << ", " -// << v(1) << "},\n"; -// { -// auto v = expoly.contour.points.front(); -// ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n"; -// } -// ss << "\t\t},\n"; - -// // Holes: -// ss << "\t\t{\n"; -// if(holes) for(auto h : expoly.holes) { -// ss << "\t\t\t{\n"; -// for(auto v : h.points) ss << "\t\t\t\t{" -// << v(0) << ", " -// << v(1) << "},\n"; -// { -// auto v = h.points.front(); -// ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n"; -// } -// ss << "\t\t\t},\n"; -// } -// ss << "\t\t},\n"; - -// ss << "\t},\n"; -// } -// } -// } - -// ss << "}\n"; - -// return ss.str(); -//} - -// Debugging: Save model to svg file. -//void toSVG(SVG& svg, const Model& model) { -// for(auto objptr : model.objects) { -// if(!objptr) continue; - -// auto rmesh = objptr->raw_mesh(); - -// for(auto objinst : objptr->instances) { -// if(!objinst) continue; - -// Slic3r::TriangleMesh tmpmesh = rmesh; -// tmpmesh.scale(objinst->get_scaling_factor()); -// objinst->transform_mesh(&tmpmesh); -// ExPolygons expolys = tmpmesh.horizontal_projection(); -// svg.draw(expolys); -// } -// } -//} - namespace bgi = boost::geometry::index; using SpatElement = std::pair; @@ -156,21 +73,6 @@ using ItemGroup = std::vector>; const double BIG_ITEM_TRESHOLD = 0.02; -Box boundingBox(const Box& pilebb, const Box& ibb ) { - auto& pminc = pilebb.minCorner(); - auto& pmaxc = pilebb.maxCorner(); - auto& iminc = ibb.minCorner(); - auto& imaxc = ibb.maxCorner(); - PointImpl minc, maxc; - - setX(minc, std::min(getX(pminc), getX(iminc))); - setY(minc, std::min(getY(pminc), getY(iminc))); - - setX(maxc, std::max(getX(pmaxc), getX(imaxc))); - setY(maxc, std::max(getY(pmaxc), getY(imaxc))); - return Box(minc, maxc); -} - // Fill in the placer algorithm configuration with values carefully chosen for // Slic3r. template @@ -194,13 +96,18 @@ void fillConfig(PConf& pcfg) { pcfg.parallel = true; } -// Type trait for an arranger class for different bin types (box, circle, -// polygon, etc...) -//template -//class AutoArranger {}; - -template clppr::IntPoint center(const Bin& bin) { return bin.center(); } -template<> clppr::IntPoint center(const clppr::Polygon &bin) { return sl::boundingBox(bin).center(); } +// Apply penality to object function result. This is used only when alignment +// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN) +double fixed_overfit(const std::tuple& result, const Box &binbb) +{ + double score = std::get<0>(result); + Box pilebb = std::get<1>(result); + Box fullbb = sl::boundingBox(pilebb, binbb); + double diff = fullbb.area() - binbb.area(); + if(diff > 0) score += diff; + + return score; +} // A class encapsulating the libnest2d Nester class and extending it with other // management and spatial index structures for acceleration. @@ -218,8 +125,7 @@ protected: Packer m_pck; PConfig m_pconf; // Placement configuration TBin m_bin; - double m_bin_area; // caching - PointImpl m_bincenter; // caching + double m_bin_area; SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_smallsrtree; // spatial index for only the smaller items double m_norm; // A coefficient to scale distances @@ -234,7 +140,7 @@ protected: // as it possibly can be but at the same time, it has to provide // reasonable results. std::tuple - objfunc(const Item &item ) + objfunc(const Item &item, const clppr::IntPoint &bincenter) { const double bin_area = m_bin_area; const SpatIndex& spatindex = m_rtree; @@ -250,7 +156,7 @@ protected: auto ibb = sl::boundingBox(item.transformedShape()); // Calculate the full bounding box of the pile with the candidate item - auto fullbb = boundingBox(m_pilebb, ibb); + auto fullbb = sl::boundingBox(m_pilebb, ibb); // The bounding box of the big items (they will accumulate in the center // of the pile @@ -286,7 +192,7 @@ protected: // The smalles distance from the arranged pile center: double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm; - double bindist = pl::distance(ibb.center(), m_bincenter) / m_norm; + double bindist = pl::distance(ibb.center(), bincenter) / m_norm; dist = 0.8*dist + 0.2*bindist; // Density is the pack density: how big is the arranged pile @@ -334,7 +240,7 @@ protected: Item& p = m_items[idx]; auto parea = p.area(); if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = boundingBox(p.boundingBox(), ibb); + auto bb = sl::boundingBox(p.boundingBox(), ibb); auto bbarea = bb.area(); auto ascore = 1.0 - (item.area() + parea)/bbarea; @@ -370,9 +276,7 @@ public: std::function stopcond) : m_pck(bin, dist) , m_bin(bin) - , m_bin_area(sl::area(bin)) - , m_bincenter(center(bin)) - , m_norm(std::sqrt(m_bin_area)) + , m_norm(std::sqrt(sl::area(bin))) { fillConfig(m_pconf); @@ -391,10 +295,10 @@ public: m_rtree.clear(); m_smallsrtree.clear(); - + // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { - return a/m_bin_area > BIG_ITEM_TRESHOLD ; + return a / m_bin_area > BIG_ITEM_TRESHOLD ; }; for(unsigned idx = 0; idx < items.size(); ++idx) { @@ -419,8 +323,11 @@ public: inline void preload(std::vector& fixeditems) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; -// m_pconf.object_function = nullptr; // drop the special objectfunction -// m_pck.preload(pg); + auto bb = sl::boundingBox(m_bin); + auto bbcenter = bb.center(); + m_pconf.object_function = [this, bb, bbcenter](const Item &item) { + return fixed_overfit(objfunc(item, bbcenter), bb); + }; // Build the rtree for queries to work @@ -442,34 +349,39 @@ public: } }; + + template<> std::function AutoArranger::get_objfn() { - return [this](const Item &itm) { - auto result = objfunc(itm); + auto bincenter = m_bin.center(); + + return [this, bincenter](const Item &itm) { + auto result = objfunc(itm, bincenter); double score = std::get<0>(result); auto& fullbb = std::get<1>(result); - + double miss = Placer::overfit(fullbb, m_bin); miss = miss > 0? miss : 0; score += miss*miss; - + return score; }; } template<> std::function AutoArranger::get_objfn() { - return [this](const Item &item) { + auto bincenter = m_bin.center(); + return [this, bincenter](const Item &item) { + + auto result = objfunc(item, bincenter); - auto result = objfunc(item); - double score = std::get<0>(result); - + auto isBig = [this](const Item& itm) { - return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; + return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ; }; - + if(isBig(item)) { auto mp = m_merged_pile; mp.push_back(item.transformedShape()); @@ -478,109 +390,26 @@ template<> std::function AutoArranger::get_objfn() if(miss < 0) miss = 0; score += miss*miss; } - + return score; }; } -template<> std::function AutoArranger::get_objfn() +// Specialization for a generalized polygon. +// Warning: this is unfinished business. It may or may not work. +template<> +std::function AutoArranger::get_objfn() { - return [this] (const Item &item) { return std::get<0>(objfunc(item)); }; + auto bincenter = sl::boundingBox(m_bin).center(); + return [this, bincenter](const Item &item) { + return std::get<0>(objfunc(item, bincenter)); + }; } -// Arranger specialization for a Box shaped bin. -//template<> class AutoArranger: public _ArrBase { -//public: - -// AutoArranger(const Box& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) -// { - -// // Here we set up the actual object function that calls the common -// // object function for all bin shapes than does an additional inside -// // check for the arranged pile. -// m_pconf.object_function = [this, bin](const Item &item) { - -// auto result = objfunc(bin.center(), item); - -// double score = std::get<0>(result); -// auto& fullbb = std::get<1>(result); - -// double miss = Placer::overfit(fullbb, bin); -// miss = miss > 0? miss : 0; -// score += miss*miss; - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - inline Circle to_lnCircle(const CircleBed& circ) { return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); } -//// Arranger specialization for circle shaped bin. -//template<> class AutoArranger: public _ArrBase { -//public: - -// AutoArranger(const Circle& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) { - -// // As with the box, only the inside check is different. -// m_pconf.object_function = [this, &bin](const Item &item) { - -// auto result = objfunc(bin.center(), item); - -// double score = std::get<0>(result); - -// auto isBig = [this](const Item& itm) { -// return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; -// }; - -// if(isBig(item)) { -// auto mp = m_merged_pile; -// mp.push_back(item.transformedShape()); -// auto chull = sl::convexHull(mp); -// double miss = Placer::overfit(chull, bin); -// if(miss < 0) miss = 0; -// score += miss*miss; -// } - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - -// Arranger specialization for a generalized polygon. -// Warning: this is unfinished business. It may or may not work. -//template<> class AutoArranger: public _ArrBase { -//public: -// AutoArranger(const PolygonImpl& bin, Distance dist, -// std::function progressind = [](unsigned){}, -// std::function stopcond = [](){return false;}): -// _ArrBase(bin, dist, progressind, stopcond) -// { -// m_pconf.object_function = [this, &bin] (const Item &item) { - -// auto binbb = sl::boundingBox(bin); -// auto result = objfunc(binbb.center(), item); -// double score = std::get<0>(result); - -// return score; -// }; - -// m_pck.configure(m_pconf); -// } -//}; - // Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; @@ -670,40 +499,46 @@ PackGroup _arrange(std::vector & shapes, { AutoArranger arranger{bin, minobjd, prind, stopfn}; - for(auto it = excludes.begin(); it != excludes.end(); ++it) - if (!sl::isInside(it->transformedShape(), bin)) - it = excludes.erase(it); - - // If there is something on the plate - if(!excludes.empty()) { -// arranger.preload(preshapes); - auto binbb = sl::boundingBox(bin); - - // Try to put the first item to the center, as the arranger will not - // do this for us. - for (auto it = shapes.begin(); it != shapes.end(); ++it) { - Item &itm = *it; - auto ibb = itm.boundingBox(); - auto d = binbb.center() - ibb.center(); - itm.translate(d); + auto it = excludes.begin(); + while (it != excludes.end()) + sl::isInside(it->transformedShape(), bin) ? + ++it : it = excludes.erase(it); - if (!arranger.is_colliding(itm)) { - itm.markAsFixed(); -// arranger.preload({{itm}}); - - // Write the transformation data into the item. The callback - // was set on the instantiation of Item and calls the - // Arrangeable interface. - it->callApplyFunction(0); - - // Remove this item, as it is arranged now - it = shapes.erase(it); - break; + // If there is something on the plate + if(!excludes.empty()) + { + arranger.preload(excludes); + auto binbb = sl::boundingBox(bin); + + // Try to put the first item to the center, as the arranger + // will not do this for us. + for (auto it = shapes.begin(); it != shapes.end(); ++it) { + Item &itm = *it; + auto ibb = itm.boundingBox(); + auto d = binbb.center() - ibb.center(); + itm.translate(d); + + if (!arranger.is_colliding(itm)) { + itm.markAsFixed(); + + // Write the transformation data into the item. The + // callback was set on the instantiation of Item and + // calls the Arrangeable interface. + it->callApplyFunction(0); + + // Remove this item, as it is arranged now + it = shapes.erase(it); + break; + } } } - } + + std::vector> inp; + inp.reserve(shapes.size() + excludes.size()); + for (auto &itm : shapes ) inp.emplace_back(itm); + for (auto &itm : excludes) inp.emplace_back(itm); - return arranger(shapes.begin(), shapes.end()); + return arranger(inp.begin(), inp.end()); } inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) @@ -713,8 +548,8 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) // The final client function for arrangement. A progress indicator and // a stop predicate can be also be passed to control the process. -bool arrange(ArrangeablePtrs & arrangables, - const ArrangeablePtrs & excludes, +bool arrange(ArrangeablePtrs & arrangables, + const ArrangeablePtrs & excludes, coord_t min_obj_distance, const BedShapeHint & bedhint, std::function progressind,