diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index e9fad405b..c51e0f5c9 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -41,25 +41,25 @@ template<> struct HolesContainer { using Type = ClipperLib::Paths; namespace pointlike { // Tell libnest2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord x(const PointImpl& p) +template<> inline ClipperLib::cInt x(const PointImpl& p) { return p.X; } // Tell libnest2d how to extract the Y coord from a ClipperPoint object -template<> inline TCoord y(const PointImpl& p) +template<> inline ClipperLib::cInt y(const PointImpl& p) { return p.Y; } // Tell libnest2d how to extract the X coord from a ClipperPoint object -template<> inline TCoord& x(PointImpl& p) +template<> inline ClipperLib::cInt& x(PointImpl& p) { return p.X; } // Tell libnest2d how to extract the Y coord from a ClipperPoint object -template<> inline TCoord& y(PointImpl& p) +template<> inline ClipperLib::cInt& y(PointImpl& p) { return p.Y; } diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 8b87229c0..5c213c110 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -166,7 +166,9 @@ public: using Tag = BoxTag; using PointType = P; - inline _Box(const P& p = {TCoord

(0), TCoord

(0)}); + inline _Box(const P& center = {TCoord

(0), TCoord

(0)}): + _Box(TCoord

(0), TCoord

(0), center) {} + inline _Box(const P& p, const P& pp): PointPair

({p, pp}) {} @@ -189,6 +191,8 @@ public: inline Unit area() const BP2D_NOEXCEPT { return Unit(width())*height(); } + + static inline _Box infinite(const P ¢er); }; template struct PointType<_Box> { @@ -463,12 +467,19 @@ inline _Box

::_Box(TCoord

width, TCoord

height, const P & center) : modulo(height, TCoord

(2))}}) {} template -inline _Box

::_Box(const P& center) { +inline _Box

_Box

::infinite(const P& center) { using C = TCoord

; - TCoord

M = std::max(getX(center), getY(center)) - - std::numeric_limits::lowest(); - maxCorner() = center + P{M, M}; - minCorner() = center - P{M, M}; + _Box

ret; + + // It is important for Mx and My to be strictly less than half of the + // range of type C. width(), height() and area() will not overflow this way. + C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 2.01); + C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 2.01); + + ret.maxCorner() = center - P{Mx, My}; + ret.minCorner() = center + P{Mx, My}; + + return ret; } template @@ -478,7 +489,7 @@ inline P _Box

::center() const BP2D_NOEXCEPT { using Coord = TCoord

; - P ret = { // No rounding here, we dont know if these are int coords + P ret = { // No rounding here, we dont know if these are int coords Coord( (getX(minc) + getX(maxc)) / Coord(2) ), Coord( (getY(minc) + getY(maxc)) / Coord(2) ) }; diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index b94443bff..4ef1fe71d 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -581,8 +581,12 @@ public: static inline double overfit(const Box& bb, const Box& bin) { - auto wdiff = double(bb.width() - bin.width()); - auto hdiff = double(bb.height() - bin.height()); + auto Bw = bin.width(); + auto Bh = bin.height(); + auto mBw = -Bw; + auto mBh = -Bh; + auto wdiff = double(bb.width()) + mBw; + auto hdiff = double(bb.height()) + mBh; double diff = 0; if(wdiff > 0) diff += wdiff; if(hdiff > 0) diff += hdiff; diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index 72a600dbb..d0c660e49 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -379,6 +379,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight) for(Item& r2 : result) { if(&r1 != &r2 ) { valid = !Item::intersects(r1, r2) || Item::touches(r1, r2); + ASSERT_TRUE(valid); valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); ASSERT_TRUE(valid); } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 2c4417b4d..84a406fd9 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -58,19 +58,24 @@ namespace arrangement { using namespace libnest2d; namespace clppr = ClipperLib; +// Get the libnest2d types for clipper backend using Item = _Item; using Box = _Box; using Circle = _Circle; using Segment = _Segment; using MultiPolygon = TMultiShape; + +// The return value of nesting, a vector (for each logical bed) of Item +// reference vectors. using PackGroup = _PackGroup; +// Summon the spatial indexing facilities from boost namespace bgi = boost::geometry::index; - using SpatElement = std::pair; using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; using ItemGroup = std::vector>; +// A coefficient used in separating bigger items and smaller items. const double BIG_ITEM_TRESHOLD = 0.02; // Fill in the placer algorithm configuration with values carefully chosen for @@ -85,14 +90,14 @@ void fillConfig(PConf& pcfg) { pcfg.starting_point = PConf::Alignment::CENTER; // TODO cannot use rotations until multiple objects of same geometry can - // handle different rotations - // arranger.useMinimumBoundigBoxRotation(); + // handle different rotations. pcfg.rotations = { 0.0 }; // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = 0.65f; - + + // Allow parallel execution. pcfg.parallel = true; } @@ -153,7 +158,7 @@ protected: }; // Candidate item bounding box - auto ibb = sl::boundingBox(item.transformedShape()); + auto ibb = item.boundingBox(); // Calculate the full bounding box of the pile with the candidate item auto fullbb = sl::boundingBox(m_pilebb, ibb); @@ -170,16 +175,39 @@ protected: // Will hold the resulting score double score = 0; - if(isBig(item.area()) || spatindex.empty()) { - // This branch is for the bigger items.. + // Density is the pack density: how big is the arranged pile + double density = 0; + + const double N = m_norm; + auto norm = [N](double val) { return val / N; }; + + // Distinction of cases for the arrangement scene + enum e_cases { + // This branch is for big items in a mixed (big and small) scene + // OR for all items in a small-only scene. + BIG_ITEM, - auto minc = ibb.minCorner(); // bottom left corner - auto maxc = ibb.maxCorner(); // top right corner + // This branch is for the last big item in a mixed scene + LAST_BIG_ITEM, + // For small items in a mixed scene. + SMALL_ITEM + } compute_case; + + bool bigitems = isBig(item.area()) || spatindex.empty(); + if(bigitems && !remaining.empty()) compute_case = BIG_ITEM; + else if (bigitems && remaining.empty()) compute_case = LAST_BIG_ITEM; + else compute_case = SMALL_ITEM; + + switch (compute_case) { + case BIG_ITEM: { + const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner + const clppr::IntPoint& 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)}; - + clppr::IntPoint top_left{getX(minc), getY(maxc)}; + clppr::IntPoint bottom_right{getX(maxc), getY(minc)}; + // Now the distance of the gravity center will be calculated to the // five anchor points and the smallest will be chosen. std::array dists; @@ -189,79 +217,75 @@ protected: dists[2] = pl::distance(ibb.center(), cc); dists[3] = pl::distance(top_left, cc); dists[4] = pl::distance(bottom_right, cc); - - // 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(), bincenter) / m_norm; - dist = 0.8*dist + 0.2*bindist; - - // Density is the pack density: how big is the arranged pile - double density = 0; - - if(remaining.empty()) { - - auto mp = m_merged_pile; - mp.emplace_back(item.transformedShape()); - auto chull = sl::convexHull(mp); - - placers::EdgeCache ec(chull); - - double circ = ec.circumference() / m_norm; - double bcirc = 2.0*(fullbb.width() + fullbb.height()) / m_norm; - score = 0.5*circ + 0.5*bcirc; - - } else { - // Prepare a variable for the alignment score. - // This will indicate: how well is the candidate item - // aligned with its neighbors. We will check the alignment - // with all neighbors and return the score for the best - // alignment. So it is enough for the candidate to be - // aligned with only one item. - auto alignment_score = 1.0; - - auto querybb = item.boundingBox(); - density = std::sqrt((fullbb.width() / m_norm )* - (fullbb.height() / m_norm)); - - // Query the spatial index for the neighbors - std::vector result; - result.reserve(spatindex.size()); - if(isBig(item.area())) { - spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } else { - smalls_spatindex.query(bgi::intersects(querybb), - std::back_inserter(result)); - } - - // now get the score for the best alignment - for(auto& e : result) { - auto idx = e.second; - Item& p = m_items[idx]; - auto parea = p.area(); - if(std::abs(1.0 - parea/item.area()) < 1e-6) { - auto bb = sl::boundingBox(p.boundingBox(), ibb); - auto bbarea = bb.area(); - auto ascore = 1.0 - (item.area() + parea)/bbarea; - - if(ascore < alignment_score) alignment_score = ascore; - } - } - // The final mix of the score is the balance between the - // distance from the full pile center, the pack density and - // the alignment with the neighbors - if (result.empty()) - score = 0.5 * dist + 0.5 * density; - else - score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; + // The smalles distance from the arranged pile center: + double dist = norm(*(std::min_element(dists.begin(), dists.end()))); + double bindist = norm(pl::distance(ibb.center(), bincenter)); + dist = 0.8 * dist + 0.2*bindist; + + // Prepare a variable for the alignment score. + // This will indicate: how well is the candidate item + // aligned with its neighbors. We will check the alignment + // with all neighbors and return the score for the best + // alignment. So it is enough for the candidate to be + // aligned with only one item. + auto alignment_score = 1.0; + + auto query = bgi::intersects(ibb); + auto& index = isBig(item.area()) ? spatindex : smalls_spatindex; + + // Query the spatial index for the neighbors + std::vector result; + result.reserve(index.size()); + + index.query(query, std::back_inserter(result)); + + // now get the score for the best alignment + for(auto& e : result) { + auto idx = e.second; + Item& p = m_items[idx]; + auto parea = p.area(); + if(std::abs(1.0 - parea/item.area()) < 1e-6) { + auto bb = sl::boundingBox(p.boundingBox(), ibb); + auto bbarea = bb.area(); + auto ascore = 1.0 - (item.area() + parea)/bbarea; + + if(ascore < alignment_score) alignment_score = ascore; + } } - } else { + + density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height())); + + // The final mix of the score is the balance between the + // distance from the full pile center, the pack density and + // the alignment with the neighbors + if (result.empty()) + score = 0.5 * dist + 0.5 * density; + else + score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; + + break; + } + case LAST_BIG_ITEM: { + auto mp = m_merged_pile; + mp.emplace_back(item.transformedShape()); + auto chull = sl::convexHull(mp); + + placers::EdgeCache ec(chull); + + double circ = norm(ec.circumference()); + double bcirc = 2.0 * norm(fullbb.width() + fullbb.height()); + score = 0.5 * circ + 0.5 * bcirc; + break; + } + case SMALL_ITEM: { // 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()) / m_norm; + score = norm(pl::distance(ibb.center(), bigbb.center())); + break; + } } return std::make_tuple(score, fullbb); @@ -276,7 +300,8 @@ public: std::function stopcond) : m_pck(bin, dist) , m_bin(bin) - , m_norm(std::sqrt(sl::area(bin))) + , m_bin_area(sl::area(bin)) + , m_norm(std::sqrt(m_bin_area)) { fillConfig(m_pconf); @@ -349,8 +374,6 @@ public: } }; - - template<> std::function AutoArranger::get_objfn() { auto bincenter = m_bin.center(); @@ -612,46 +635,51 @@ bool arrange(ArrangeablePtrs & arrangables, auto& cfn = stopcondition; switch (bedhint.type) { - case BedShapeType::BOX: { - // Create the arranger for the box shaped bed - BoundingBox bbb = bedhint.shape.box; - bbb.min -= Point{md, md}, bbb.max += Point{md, md}; - Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; - binwidth = coord_t(binbb.width()); +// case BedShapeType::BOX: { +// // Create the arranger for the box shaped bed +// BoundingBox bbb = bedhint.shape.box; +// bbb.min -= Point{md, md}, bbb.max += Point{md, md}; +// Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; +// binwidth = coord_t(binbb.width()); - _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::CIRCLE: { - auto c = bedhint.shape.circ; - auto cc = to_lnCircle(c); - binwidth = scaled(c.radius()); +// _arrange(items, fixeditems, binbb, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::CIRCLE: { +// auto c = bedhint.shape.circ; +// auto cc = to_lnCircle(c); +// binwidth = scaled(c.radius()); - _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::IRREGULAR: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); - auto irrbed = sl::create(std::move(ctour)); - BoundingBox polybb(bedhint.shape.polygon); - binwidth = (polybb.max(X) - polybb.min(X)); +// _arrange(items, fixeditems, cc, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::IRREGULAR: { +// auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon); +// auto irrbed = sl::create(std::move(ctour)); +// BoundingBox polybb(bedhint.shape.polygon); +// binwidth = (polybb.max(X) - polybb.min(X)); - _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); - break; - } - case BedShapeType::INFINITE: { - // const InfiniteBed& nobin = bedhint.shape.infinite; - //Box infbb{{nobin.center.x(), nobin.center.y()}}; - Box infbb; +// _arrange(items, fixeditems, irrbed, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::INFINITE: { +// const InfiniteBed& nobin = bedhint.shape.infinite; +// Box infbb{{nobin.center.x(), nobin.center.y()}}; +// _arrange(items, fixeditems, infbb, min_obj_distance, progressind, cfn); +// break; +// } +// case BedShapeType::UNKNOWN: { +// // We know nothing about the bed, let it be infinite and zero centered +// _arrange(items, fixeditems, Box{}, min_obj_distance, progressind, cfn); +// break; +// } + default: { + Box infbb = Box::infinite({bedhint.shape.box.center().x(), bedhint.shape.box.center().y()}); + _arrange(items, fixeditems, infbb, min_obj_distance, progressind, cfn); break; } - case BedShapeType::UNKNOWN: { - // We know nothing about the bed, let it be infinite and zero centered - _arrange(items, fixeditems, Box{}, min_obj_distance, progressind, cfn); - break; - } }; if(stopcondition()) return false; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index fd48dcec4..2af4e4c27 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -398,6 +398,9 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) // } // o->invalidate_bounding_box(); // } + +// return true; + size_t count = 0; for (auto obj : objects) count += obj->instances.size();