From a7ba51bd111a8a38fda5a98fc82e12145c793d35 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 2 Aug 2018 13:15:30 +0200 Subject: [PATCH] Fixing the "last item doesn't fit" problem. --- .../libnest2d/libnest2d/geometry_traits.hpp | 31 ++ xs/src/libnest2d/libnest2d/libnest2d.hpp | 3 +- .../libnest2d/libnest2d/placers/nfpplacer.hpp | 75 +-- xs/src/libslic3r/ModelArrange.hpp | 490 +++++++++++------- xs/src/slic3r/AppController.cpp | 19 +- 5 files changed, 399 insertions(+), 219 deletions(-) diff --git a/xs/src/libnest2d/libnest2d/geometry_traits.hpp b/xs/src/libnest2d/libnest2d/geometry_traits.hpp index 99511d775..00740f30c 100644 --- a/xs/src/libnest2d/libnest2d/geometry_traits.hpp +++ b/xs/src/libnest2d/libnest2d/geometry_traits.hpp @@ -684,6 +684,20 @@ struct ShapeLike { return PointLike::distance(point, circ.center()) < circ.radius(); } + template + static bool isInside(const TPoint& point, + const _Box>& box) + { + auto px = getX(point); + auto py = getY(point); + auto minx = getX(box.minCorner()); + auto miny = getY(box.minCorner()); + auto maxx = getX(box.maxCorner()); + auto maxy = getY(box.maxCorner()); + + return px > minx && px < maxx && py > miny && py < maxy; + } + template static bool isInside(const RawShape& sh, const _Circle>& circ) @@ -702,6 +716,23 @@ struct ShapeLike { isInside(box.maxCorner(), circ); } + template + static bool isInside(const _Box>& ibb, + const _Box>& box) + { + auto iminX = getX(ibb.minCorner()); + auto imaxX = getX(ibb.maxCorner()); + auto iminY = getY(ibb.minCorner()); + auto imaxY = getY(ibb.maxCorner()); + + auto minX = getX(box.minCorner()); + auto maxX = getX(box.maxCorner()); + auto minY = getY(box.minCorner()); + auto maxY = getY(box.maxCorner()); + + return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY; + } + template // Potential O(1) implementation may exist static inline TPoint& vertex(RawShape& sh, unsigned long idx) { diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp index fad38b9a3..7f23de358 100644 --- a/xs/src/libnest2d/libnest2d/libnest2d.hpp +++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp @@ -473,8 +473,7 @@ public: template inline bool _Item::isInside(const _Box>& box) const { - _Rectangle rect(box.width(), box.height()); - return _Item::isInside(rect); + return ShapeLike::isInside(boundingBox(), box); } template inline bool diff --git a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp index 06163b00a..0c5776400 100644 --- a/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp +++ b/xs/src/libnest2d/libnest2d/placers/nfpplacer.hpp @@ -166,7 +166,7 @@ template class EdgeCache { using std::pow; return static_cast( - round( N/(ceil(pow(accuracy_, 2)*(N-1)) + 1) ) + std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0))) ); } @@ -178,6 +178,7 @@ template class EdgeCache { contour_.corners.reserve(N / S + 1); auto N_1 = N-1; + contour_.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { contour_.corners.emplace_back( contour_.distances.at(i) / contour_.full_distance); @@ -192,6 +193,7 @@ template class EdgeCache { const auto S = stride(N); auto N_1 = N-1; hc.corners.reserve(N / S + 1); + hc.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { hc.corners.emplace_back( hc.distances.at(i) / hc.full_distance); @@ -484,7 +486,7 @@ public: bool static inline wouldFit(const RawShape& chull, const RawShape& bin) { auto bbch = sl::boundingBox(chull); auto bbin = sl::boundingBox(bin); - auto d = bbin.center() - bbch.center(); + auto d = bbch.center() - bbin.center(); auto chullcpy = chull; sl::translate(chullcpy, d); return sl::isInside(chullcpy, bin); @@ -579,17 +581,21 @@ public: pile_area += mitem.area(); } + auto merged_pile = Nfp::merge(pile); + // This is the kernel part of the object function that is // customizable by the library client auto _objfunc = config_.object_function? config_.object_function : - [this](Nfp::Shapes& pile, const Item& item, - double occupied_area, double /*norm*/, - double penality) + [this, &merged_pile]( + Nfp::Shapes& /*pile*/, + const Item& item, + double occupied_area, double norm, + double /*penality*/) { - pile.emplace_back(item.transformedShape()); - auto ch = sl::convexHull(pile); - pile.pop_back(); + merged_pile.emplace_back(item.transformedShape()); + auto ch = sl::convexHull(merged_pile); + merged_pile.pop_back(); // The pack ratio -- how much is the convex hull occupied double pack_rate = occupied_area/sl::area(ch); @@ -602,7 +608,7 @@ public: // (larger) values. auto score = std::sqrt(waste); - if(!wouldFit(ch, bin_)) score = 2*penality - score; + if(!wouldFit(ch, bin_)) score += norm; return score; }; @@ -622,9 +628,22 @@ public: return score; }; + auto boundaryCheck = [&](const Optimum& o) { + auto v = getNfpPoint(o); + auto d = v - iv; + d += startpos; + item.translation(d); + + merged_pile.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(merged_pile); + merged_pile.pop_back(); + + return wouldFit(chull, bin_); + }; + opt::StopCriteria stopcr; - stopcr.max_iterations = 1000; - stopcr.absolute_score_difference = 1e-20*norm_; + stopcr.max_iterations = 100; + stopcr.relative_score_difference = 1e-6; opt::TOptimizer solver(stopcr); Optimum optimum(0, 0); @@ -644,7 +663,7 @@ public: std::for_each(cache.corners().begin(), cache.corners().end(), [ch, &contour_ofn, &solver, &best_score, - &optimum] (double pos) + &optimum, &boundaryCheck] (double pos) { try { auto result = solver.optimize_min(contour_ofn, @@ -653,22 +672,15 @@ public: ); if(result.score < best_score) { - best_score = result.score; - optimum.relpos = std::get<0>(result.optimum); - optimum.nfpidx = ch; - optimum.hidx = -1; + Optimum o(std::get<0>(result.optimum), ch, -1); + if(boundaryCheck(o)) { + best_score = result.score; + optimum = o; + } } } catch(std::exception& e) { derr() << "ERROR: " << e.what() << "\n"; } - -// auto sc = contour_ofn(pos); -// if(sc < best_score) { -// best_score = sc; -// optimum.relpos = pos; -// optimum.nfpidx = ch; -// optimum.hidx = -1; -// } }); for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) { @@ -683,7 +695,7 @@ public: std::for_each(cache.corners(hidx).begin(), cache.corners(hidx).end(), [&hole_ofn, &solver, &best_score, - &optimum, ch, hidx] + &optimum, ch, hidx, &boundaryCheck] (double pos) { try { @@ -693,21 +705,16 @@ public: ); if(result.score < best_score) { - best_score = result.score; Optimum o(std::get<0>(result.optimum), ch, hidx); - optimum = o; + if(boundaryCheck(o)) { + best_score = result.score; + optimum = o; + } } } catch(std::exception& e) { derr() << "ERROR: " << e.what() << "\n"; } -// auto sc = hole_ofn(pos); -// if(sc < best_score) { -// best_score = sc; -// optimum.relpos = pos; -// optimum.nfpidx = ch; -// optimum.hidx = hidx; -// } }); } } diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp index af4bfcf70..73dc83c57 100644 --- a/xs/src/libslic3r/ModelArrange.hpp +++ b/xs/src/libslic3r/ModelArrange.hpp @@ -93,6 +93,237 @@ void toSVG(SVG& svg, const Model& model) { } } +std::tuple +objfunc(const PointImpl& bincenter, + ShapeLike::Shapes& pile, // The currently arranged pile + const Item &item, + double norm // A norming factor for physical dimensions + ) +{ + using pl = PointLike; + + static const double BIG_ITEM_TRESHOLD = 0.2; + static const double ROUNDNESS_RATIO = 0.5; + static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; + + // We will treat big items (compared to the print bed) differently + NfpPlacer::Pile bigs; + bigs.reserve(pile.size()); + for(auto& p : pile) { + auto pbb = ShapeLike::boundingBox(p); + auto na = std::sqrt(pbb.width()*pbb.height())/norm; + if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p); + } + + // Candidate item bounding box + auto ibb = item.boundingBox(); + + // Calculate the full bounding box of the pile with the candidate item + pile.emplace_back(item.transformedShape()); + auto fullbb = ShapeLike::boundingBox(pile); + pile.pop_back(); + + // The bounding box of the big items (they will accumulate in the center + // of the pile + auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs); + + // The size indicator of the candidate item. This is not the area, + // but almost... + auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm; + + // Will hold the resulting score + double score = 0; + + if(itemnormarea > BIG_ITEM_TRESHOLD) { + // This branch is for the bigger items.. + // Here we will use the closest point of the item bounding box to + // the already arranged pile. So not the bb center nor the a choosen + // corner but whichever is the closest to the center. This will + // prevent unwanted strange arrangements. + + // Now the distance of the gravity center will be calculated to the + // five anchor points and the smallest will be chosen. + + auto minc = ibb.minCorner(); // bottom left corner + auto maxc = ibb.maxCorner(); // top right corner + + // top left and bottom right corners + auto top_left = PointImpl{getX(minc), getY(maxc)}; + auto bottom_right = PointImpl{getX(maxc), getY(minc)}; + + // Now the distnce of the gravity center will be calculated to the + // five anchor points and the smallest will be chosen. + std::array dists; + auto cc = fullbb.center(); // The gravity center + dists[0] = pl::distance(minc, cc); + dists[1] = pl::distance(maxc, cc); + dists[2] = pl::distance(ibb.center(), cc); + dists[3] = pl::distance(top_left, cc); + dists[4] = pl::distance(bottom_right, cc); + + auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; + + // Density is the pack density: how big is the arranged pile + auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; + + // The score is a weighted sum of the distance from pile center + // and the pile size + score = ROUNDNESS_RATIO * dist + DENSITY_RATIO * density; + + } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) { + // If there are no big items, only small, we should consider the + // density here as well to not get silly results + auto bindist = pl::distance(ibb.center(), bincenter) / norm; + auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; + score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density; + } else { + // Here there are the small items that should be placed around the + // already processed bigger items. + // No need to play around with the anchor points, the center will be + // just fine for small items + score = pl::distance(ibb.center(), bigbb.center()) / norm; + } + + return std::make_tuple(score, fullbb); +} + +template +void fillConfig(PConf& pcfg) { + + // Align the arranged pile into the center of the bin + pcfg.alignment = PConf::Alignment::CENTER; + + // Start placing the items from the center of the print bed + pcfg.starting_point = PConf::Alignment::CENTER; + + // TODO cannot use rotations until multiple objects of same geometry can + // handle different rotations + // arranger.useMinimumBoundigBoxRotation(); + pcfg.rotations = { 0.0 }; + + // The accuracy of optimization. + // Goes from 0.0 to 1.0 and scales performance as well + pcfg.accuracy = 0.35f; +} + +template +class AutoArranger {}; + +template +class _ArrBase { +protected: + using Placer = strategies::_NofitPolyPlacer; + using Selector = FirstFitSelection; + using Packer = Arranger; + using PConfig = typename Packer::PlacementConfig; + using Distance = TCoord; + using Pile = ShapeLike::Shapes; + + Packer pck_; + PConfig pconf_; // Placement configuration + +public: + + _ArrBase(const TBin& bin, Distance dist, + std::function progressind): + pck_(bin, dist) + { + fillConfig(pconf_); + pck_.progressIndicator(progressind); + } + + template inline IndexedPackGroup operator()(Args&&...args) { + return pck_.arrangeIndexed(std::forward(args)...); + } +}; + +template<> +class AutoArranger: public _ArrBase { +public: + + AutoArranger(const Box& bin, Distance dist, + std::function progressind): + _ArrBase(bin, dist, progressind) + { + pconf_.object_function = [bin] ( + Pile& pile, + const Item &item, + double /*occupied_area*/, + double norm, + double penality) { + + auto result = objfunc(bin.center(), pile, item, norm); + double score = std::get<0>(result); + auto& fullbb = std::get<1>(result); + + auto wdiff = fullbb.width() - bin.width(); + auto hdiff = fullbb.height() - bin.height(); + if(wdiff > 0) score += std::pow(wdiff, 2) / norm; + if(hdiff > 0) score += std::pow(hdiff, 2) / norm; + + return score; + }; + + pck_.configure(pconf_); + } +}; + +template<> +class AutoArranger: public _ArrBase { +public: + AutoArranger(const PolygonImpl& bin, Distance dist, + std::function progressind): + _ArrBase(bin, dist, progressind) + { + pconf_.object_function = [&bin] ( + Pile& pile, + const Item &item, + double /*area*/, + double norm, + double /*penality*/) { + + auto binbb = ShapeLike::boundingBox(bin); + auto result = objfunc(binbb.center(), pile, item, norm); + double score = std::get<0>(result); + + pile.emplace_back(item.transformedShape()); + auto chull = ShapeLike::convexHull(pile); + pile.pop_back(); + + // If it does not fit into the print bed we will beat it with a + // large penality. If we would not do this, there would be only one + // big pile that doesn't care whether it fits onto the print bed. + if(!Placer::wouldFit(chull, bin)) score += norm; + + return score; + }; + + pck_.configure(pconf_); + } +}; + +template<> // Specialization with no bin +class AutoArranger: public _ArrBase { +public: + + AutoArranger(Distance dist, std::function progressind): + _ArrBase(Box(0, 0), dist, progressind) + { + this->pconf_.object_function = [] ( + Pile& pile, + const Item &item, + double /*area*/, + double norm, + double /*penality*/) { + + auto result = objfunc({0, 0}, pile, item, norm); + return std::get<0>(result); + }; + + this->pck_.configure(pconf_); + } +}; + // A container which stores a pointer to the 3D object and its projected // 2D shape from top view. using ShapeData2D = @@ -147,6 +378,44 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { return ret; } +enum BedShapeHint { + BOX, + CIRCLE, + IRREGULAR, + WHO_KNOWS +}; + +BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) { + // Determine the bed shape by hand + return BOX; +} + +void applyResult( + IndexedPackGroup::value_type& group, + Coord batch_offset, + ShapeData2D& shapemap) +{ + for(auto& r : group) { + auto idx = r.first; // get the original item index + Item& item = r.second; // get the item itself + + // Get the model instance from the shapemap using the index + ModelInstance *inst_ptr = shapemap[idx].first; + + // Get the tranformation data from the item object and scale it + // appropriately + auto off = item.translation(); + Radians rot = item.rotation(); + Pointf foff(off.X*SCALING_FACTOR + batch_offset, + off.Y*SCALING_FACTOR); + + // write the tranformation data into the model instance + inst_ptr->rotation = rot; + inst_ptr->offset = foff; + } +} + + /** * \brief Arranges the model objects on the screen. * @@ -170,7 +439,9 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { * bed or leave them untouched (let the user arrange them by hand or remove * them). */ -bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb, +bool arrange(Model &model, coordf_t min_obj_distance, + const Slic3r::Polyline& bed, + BedShapeHint bedhint, bool first_bin_only, std::function progressind) { @@ -178,215 +449,74 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb, bool ret = true; - // Create the arranger config - auto min_obj_distance = static_cast(dist/SCALING_FACTOR); - // Get the 2D projected shapes with their 3D model instance pointers auto shapemap = arr::projectModelFromTop(model); - bool hasbin = bb != nullptr && bb->defined; - double area_max = 0; - // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon std::vector> shapes; shapes.reserve(shapemap.size()); std::for_each(shapemap.begin(), shapemap.end(), - [&shapes, min_obj_distance, &area_max, hasbin] - (ShapeData2D::value_type& it) + [&shapes] (ShapeData2D::value_type& it) { shapes.push_back(std::ref(it.second)); }); - Box bin; + IndexedPackGroup result; + BoundingBox bbb(bed.points); - if(hasbin) { - // Scale up the bounding box to clipper scale. - BoundingBoxf bbb = *bb; - bbb.scale(1.0/SCALING_FACTOR); + auto binbb = Box({ + static_cast(bbb.min.x), + static_cast(bbb.min.y) + }, + { + static_cast(bbb.max.x), + static_cast(bbb.max.y) + }); - bin = Box({ - static_cast(bbb.min.x), - static_cast(bbb.min.y) - }, - { - static_cast(bbb.max.x), - static_cast(bbb.max.y) - }); + switch(bedhint) { + case BOX: { + + // Create the arranger for the box shaped bed + AutoArranger arrange(binbb, min_obj_distance, progressind); + + // Arrange and return the items with their respective indices within the + // input sequence. + result = arrange(shapes.begin(), shapes.end()); + break; } + case CIRCLE: + break; + case IRREGULAR: + case WHO_KNOWS: { + using P = libnest2d::PolygonImpl; - // Will use the DJD selection heuristic with the BottomLeft placement - // strategy - using Arranger = Arranger; - using PConf = Arranger::PlacementConfig; - using SConf = Arranger::SelectionConfig; + auto ctour = Slic3rMultiPoint_to_ClipperPath(bed); + P irrbed = ShapeLike::create(std::move(ctour)); - PConf pcfg; // Placement configuration - SConf scfg; // Selection configuration + std::cout << ShapeLike::toString(irrbed) << std::endl; - // Align the arranged pile into the center of the bin - pcfg.alignment = PConf::Alignment::CENTER; + AutoArranger

arrange(irrbed, min_obj_distance, progressind); - // Start placing the items from the center of the print bed - pcfg.starting_point = PConf::Alignment::CENTER; - - // TODO cannot use rotations until multiple objects of same geometry can - // handle different rotations - // arranger.useMinimumBoundigBoxRotation(); - pcfg.rotations = { 0.0 }; - - // The accuracy of optimization. Goes from 0.0 to 1.0 and scales performance - pcfg.accuracy = 0.4f; - - // Magic: we will specify what is the goal of arrangement... In this case - // we override the default object function to make the larger items go into - // the center of the pile and smaller items orbit it so the resulting pile - // has a circle-like shape. This is good for the print bed's heat profile. - // We alse sacrafice a bit of pack efficiency for this to work. As a side - // effect, the arrange procedure is a lot faster (we do not need to - // calculate the convex hulls) - pcfg.object_function = [bin, hasbin]( - NfpPlacer::Pile& pile, // The currently arranged pile - const Item &item, - double /*area*/, // Sum area of items (not needed) - double norm, // A norming factor for physical dimensions - double penality) // Min penality in case of bad arrangement - { - using pl = PointLike; - - static const double BIG_ITEM_TRESHOLD = 0.2; - static const double GRAVITY_RATIO = 0.5; - static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO; - - // We will treat big items (compared to the print bed) differently - NfpPlacer::Pile bigs; - bigs.reserve(pile.size()); - for(auto& p : pile) { - auto pbb = ShapeLike::boundingBox(p); - auto na = std::sqrt(pbb.width()*pbb.height())/norm; - if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p); - } - - // Candidate item bounding box - auto ibb = item.boundingBox(); - - // Calculate the full bounding box of the pile with the candidate item - pile.emplace_back(item.transformedShape()); - auto fullbb = ShapeLike::boundingBox(pile); - pile.pop_back(); - - // The bounding box of the big items (they will accumulate in the center - // of the pile - auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs); - - // The size indicator of the candidate item. This is not the area, - // but almost... - auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm; - - // Will hold the resulting score - double score = 0; - - if(itemnormarea > BIG_ITEM_TRESHOLD) { - // This branch is for the bigger items.. - // Here we will use the closest point of the item bounding box to - // the already arranged pile. So not the bb center nor the a choosen - // corner but whichever is the closest to the center. This will - // prevent unwanted strange arrangements. - - auto minc = ibb.minCorner(); // bottom left corner - auto maxc = ibb.maxCorner(); // top right corner - - // top left and bottom right corners - auto top_left = PointImpl{getX(minc), getY(maxc)}; - auto bottom_right = PointImpl{getX(maxc), getY(minc)}; - - auto cc = fullbb.center(); // The gravity center - - // Now the distnce of the gravity center will be calculated to the - // five anchor points and the smallest will be chosen. - std::array dists; - dists[0] = pl::distance(minc, cc); - dists[1] = pl::distance(maxc, cc); - dists[2] = pl::distance(ibb.center(), cc); - dists[3] = pl::distance(top_left, cc); - dists[4] = pl::distance(bottom_right, cc); - - auto dist = *(std::min_element(dists.begin(), dists.end())) / norm; - - // Density is the pack density: how big is the arranged pile - auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; - - // The score is a weighted sum of the distance from pile center - // and the pile size - score = GRAVITY_RATIO * dist + DENSITY_RATIO * density; - - } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) { - // If there are no big items, only small, we should consider the - // density here as well to not get silly results - auto bindist = pl::distance(ibb.center(), bin.center()) / norm; - auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm; - score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density; - } else { - // Here there are the small items that should be placed around the - // already processed bigger items. - // No need to play around with the anchor points, the center will be - // just fine for small items - score = pl::distance(ibb.center(), bigbb.center()) / norm; - } - - // If it does not fit into the print bed we will beat it - // with a large penality. If we would not do this, there would be only - // one big pile that doesn't care whether it fits onto the print bed. - if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score; - - return score; - }; - - // Create the arranger object - Arranger arranger(bin, min_obj_distance, pcfg, scfg); - - // Set the progress indicator for the arranger. - arranger.progressIndicator(progressind); - - // Arrange and return the items with their respective indices within the - // input sequence. - auto result = arranger.arrangeIndexed(shapes.begin(), shapes.end()); - - auto applyResult = [&shapemap](ArrangeResult::value_type& group, - Coord batch_offset) - { - for(auto& r : group) { - auto idx = r.first; // get the original item index - Item& item = r.second; // get the item itself - - // Get the model instance from the shapemap using the index - ModelInstance *inst_ptr = shapemap[idx].first; - - // Get the tranformation data from the item object and scale it - // appropriately - auto off = item.translation(); - Radians rot = item.rotation(); - Pointf foff(off.X*SCALING_FACTOR + batch_offset, - off.Y*SCALING_FACTOR); - - // write the tranformation data into the model instance - inst_ptr->rotation = rot; - inst_ptr->offset = foff; - } + // Arrange and return the items with their respective indices within the + // input sequence. + result = arrange(shapes.begin(), shapes.end()); + break; + } }; if(first_bin_only) { - applyResult(result.front(), 0); + applyResult(result.front(), 0, shapemap); } else { const auto STRIDE_PADDING = 1.2; Coord stride = static_cast(STRIDE_PADDING* - bin.width()*SCALING_FACTOR); + binbb.width()*SCALING_FACTOR); Coord batch_offset = 0; for(auto& group : result) { - applyResult(group, batch_offset); + applyResult(group, batch_offset, shapemap); // Only the first pack group can be placed onto the print bed. The // other objects which could not fit will be placed next to the diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 1d4b7d545..58858f5fc 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -294,6 +294,8 @@ void AppController::arrange_model() supports_asynch()? std::launch::async : std::launch::deferred, [this]() { + using Coord = libnest2d::TCoord; + unsigned count = 0; for(auto obj : model_->objects) count += obj->instances.size(); @@ -311,14 +313,25 @@ void AppController::arrange_model() auto dist = print_ctl()->config().min_object_distance(); + // Create the arranger config + auto min_obj_distance = static_cast(dist/SCALING_FACTOR); - BoundingBoxf bb(print_ctl()->config().bed_shape.values); + auto& bedpoints = print_ctl()->config().bed_shape.values; + Polyline bed; bed.points.reserve(bedpoints.size()); + for(auto& v : bedpoints) + bed.append(Point::new_scale(v.x, v.y)); if(pind) pind->update(0, _(L("Arranging objects..."))); try { - arr::arrange(*model_, dist, &bb, false, [pind, count](unsigned rem){ - if(pind) pind->update(count - rem, _(L("Arranging objects..."))); + arr::arrange(*model_, + min_obj_distance, + bed, + arr::BOX, + false, // create many piles not just one pile + [pind, count](unsigned rem) { + if(pind) + pind->update(count - rem, _(L("Arranging objects..."))); }); } catch(std::exception& e) { std::cerr << e.what() << std::endl;