Bug fixes for the neighborhood detection

This commit is contained in:
tamasmeszaros 2018-08-07 19:48:00 +02:00
parent 08fb677583
commit 20b7aad6d1
12 changed files with 190 additions and 206 deletions

View File

@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already
existing implementation to avoid copying or having unnecessary dependencies.
A default backend is provided if the user of the library just wants to use it
out of the box without additional integration. The default backend is reasonably
out of the box without additional integration. This backend is reasonably
fast and robust, being built on top of boost geometry and the
[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of
this default backend implies the dependency on these packages as well as the
compilation of the backend itself (The default backend is not yet header only).
this default backend implies the dependency on these packages but its header
only as well.
This software is currently under construction and lacks a throughout
documentation and some essential algorithms as well. At this stage it works well
for rectangles and convex closed polygons without considering holes and
concavities.
Holes and non-convex polygons will be usable in the near future as well.
Holes and non-convex polygons will be usable in the near future as well. The
no fit polygon based placer module combined with the first fit selection
strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r)
application's arrangement feature. It uses local optimization techniques to find
the best placement of each new item based on some features of the arrangement.
In the near future I would like to use machine learning to evaluate the
placements and (or) the order if items in which they are placed and see what
results can be obtained. This is a different approach than that of SVGnest which
uses genetic algorithms to find better and better selection orders. Maybe the
two approaches can be combined as well.
# References
- [SVGNest](https://github.com/Jack000/SVGnest)

View File

@ -95,98 +95,6 @@ void arrangeRectangles() {
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
pconf.accuracy = 1.0f;
// auto bincenter = ShapeLike::boundingBox<PolygonImpl>(bin).center();
// pconf.object_function = [&bin, bincenter](
// Placer::Pile pile, const Item& item,
// double /*area*/, double norm, double penality) {
// 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<double, 5> 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(), bincenter) / 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(!Placer::wouldFit(fullbb, bin)) score += norm;
// return score;
// };
Packer::SelectionConfig sconf;
// sconf.allow_parallel = false;
// sconf.force_parallel = false;

View File

@ -313,9 +313,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
using Coord = TCoord<RawPoint>;
RawPoint ret = {
static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ),
static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) )
RawPoint ret = { // No rounding here, we dont know if these are int coords
static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ),
static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 )
};
return ret;

View File

@ -541,21 +541,20 @@ public:
inline void configure(const Config& config) { impl_.configure(config); }
/**
* @brief A method that tries to pack an item and returns an object
* describing the pack result.
* Try to pack an item with a result object that contains the packing
* information for later accepting it.
*
* The result can be casted to bool and used as an argument to the accept
* method to accept a succesfully packed item. This way the next packing
* will consider the accepted item as well. The PackResult should carry the
* transformation info so that if the tried item is later modified or tried
* multiple times, the result object should set it to the originally
* determied position. An implementation can be found in the
* strategies::PlacerBoilerplate::PackResult class.
*
* @param item Ithe item to be packed.
* @return The PackResult object that can be implicitly casted to bool.
* \param item_store A container of items
*/
inline PackResult trypack(Item& item) { return impl_.trypack(item); }
template<class Container>
inline PackResult trypack(Container& item_store,
typename Container::iterator from,
unsigned count = 1) {
using V = typename Container::value_type;
static_assert(std::is_convertible<V, const Item&>::value,
"Invalid Item container!");
return impl_.trypack(item_store, from, count);
}
/**
* @brief A method to accept a previously tried item.
@ -578,7 +577,16 @@ public:
* @return Returns true if the item was packed or false if it could not be
* packed.
*/
inline bool pack(Item& item) { return impl_.pack(item); }
template<class Container>
inline bool pack(Container& item_store,
typename Container::iterator from,
unsigned count = 1)
{
using V = typename Container::value_type;
static_assert(std::is_convertible<V, const Item&>::value,
"Invalid Item container!");
return impl_.pack(item_store, from, count);
}
/// Unpack the last element (remove it from the list of packed items).
inline void unpackLast() { impl_.unpackLast(); }

View File

@ -27,9 +27,14 @@ public:
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
PackResult trypack(Item& item) {
template<class Store>
PackResult trypack(Store& /*s*/, typename Store::iterator from,
unsigned /*count*/ = 1)
{
Item& item = *from;
auto r = _trypack(item);
if(!r && Base::config_.allow_rotations) {
item.rotate(Degrees(90));
r =_trypack(item);
}

View File

@ -19,6 +19,8 @@ namespace libnest2d { namespace strategies {
template<class RawShape>
struct NfpPConfig {
using ItemGroup = std::vector<std::reference_wrapper<_Item<RawShape>>>;
enum class Alignment {
CENTER,
BOTTOM_LEFT,
@ -57,8 +59,8 @@ struct NfpPConfig {
* \param item The second parameter is the candidate item.
*
* \param occupied_area The third parameter is the sum of areas of the
* items in the first parameter so you don't have to iterate through them
* if you only need their area.
* items in the first parameter (no candidate item there) so you don't have
* to iterate through them if you only need their accumulated area.
*
* \param norm A norming factor for physical dimensions. E.g. if your score
* is the distance between the item and the bin center, you should divide
@ -66,21 +68,21 @@ struct NfpPConfig {
* divide it with the square of the norming factor. Imagine it as a unit of
* distance.
*
* \param penality The fifth parameter is the amount of minimum penality if
* the arranged pile would't fit into the bin. You can use the wouldFit()
* function to check this. Note that the pile can be outside the bin's
* boundaries while the placement algorithm is running. Your job is only to
* check if the pile could be translated into a position in the bin where
* all the items would be inside. For a box shaped bin you can use the
* pile's bounding box to check whether it's width and height is small
* enough. If the pile would not fit, you have to make sure that the
* resulting score will be higher then the penality value. A good solution
* would be to set score = 2*penality-score in case the pile wouldn't fit
* into the bin.
* \param remaining A container with the remaining items waiting to be
* placed. You can use some features about the remaining items to alter to
* score of the current placement. If you know that you have to leave place
* for other items as well, that might influence your decision about where
* the current candidate should be placed. E.g. imagine three big circles
* which you want to place into a box: you might place them in a triangle
* shape which has the maximum pack density. But if there is a 4th big
* circle than you won't be able to pack it. If you knew apriori that
* there four circles are to be placed, you would have placed the first 3
* into an L shape. This parameter can be used to make these kind of
* decisions (for you or a more intelligent AI).
*
*/
std::function<double(Nfp::Shapes<RawShape>&, const _Item<RawShape>&,
double, double, double)>
double, double, const ItemGroup&)>
object_function;
/**
@ -450,11 +452,13 @@ _Circle<TPoint<RawShape>> minimizeCircle(const RawShape& sh) {
using Point = TPoint<RawShape>;
using Coord = TCoord<Point>;
auto& ctr = sl::getContour(sh);
if(ctr.empty()) return {{0, 0}, 0};
auto bb = sl::boundingBox(sh);
auto capprx = bb.center();
auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner());
auto& ctr = sl::getContour(sh);
opt::StopCriteria stopcr;
stopcr.max_iterations = 100;
@ -513,7 +517,6 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
using Box = _Box<TPoint<RawShape>>;
const double norm_;
const double penality_;
using MaxNfpLevel = Nfp::MaxNfpLevel<RawShape>;
using sl = ShapeLike;
@ -524,8 +527,7 @@ public:
inline explicit _NofitPolyPlacer(const BinType& bin):
Base(bin),
norm_(std::sqrt(sl::area<RawShape>(bin))),
penality_(1e6*norm_) {}
norm_(std::sqrt(sl::area<RawShape>(bin))) {}
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
@ -575,7 +577,15 @@ public:
return boundingCircle(chull).radius() < bin.radius();
}
PackResult trypack(Item& item) {
template<class Container>
PackResult trypack(Container& items,
typename Container::iterator from,
unsigned /*count*/ = 1)
{
return trypack(*from, {std::next(from), items.end()});
}
PackResult trypack(Item& item, ItemGroup remaining) {
PackResult ret;
@ -586,7 +596,7 @@ public:
can_pack = item.isInside(bin_);
} else {
double global_score = penality_;
double global_score = std::numeric_limits<double>::max();
auto initial_tr = item.translation();
auto initial_rot = item.rotation();
@ -630,9 +640,8 @@ public:
auto getNfpPoint = [&ecache](const Optimum& opt)
{
auto ret = opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
return ret;
};
Nfp::Shapes<RawShape> pile;
@ -654,7 +663,7 @@ public:
const Item& item,
double occupied_area,
double norm,
double /*penality*/)
const ItemGroup& /*remaining*/)
{
merged_pile.emplace_back(item.transformedShape());
auto ch = sl::convexHull(merged_pile);
@ -686,7 +695,7 @@ public:
double occupied_area = pile_area + item.area();
double score = _objfunc(pile, item, occupied_area,
norm_, penality_);
norm_, remaining);
return score;
};
@ -705,12 +714,12 @@ public:
};
opt::StopCriteria stopcr;
stopcr.max_iterations = 100;
stopcr.relative_score_difference = 1e-12;
stopcr.max_iterations = 200;
stopcr.relative_score_difference = 1e-20;
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
Optimum optimum(0, 0);
double best_score = penality_;
double best_score = std::numeric_limits<double>::max();
// Local optimization with the four polygon corners as
// starting points
@ -821,7 +830,6 @@ private:
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
if(items_.empty()) return;
Nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
@ -833,6 +841,7 @@ private:
}
inline void finalAlign(Box bbin) {
if(items_.empty()) return;
Nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());

View File

@ -56,8 +56,11 @@ public:
config_ = config;
}
bool pack(Item& item) {
auto&& r = static_cast<Subclass*>(this)->trypack(item);
template<class Container>
bool pack(Container& items,
typename Container::iterator from,
unsigned count = 1) {
auto&& r = static_cast<Subclass*>(this)->trypack(items, from, count);
if(r) {
items_.push_back(*(r.item_ptr_));
farea_valid_ = false;

View File

@ -230,7 +230,7 @@ public:
while(it != not_packed.end() && !ret &&
free_area - (item_area = it->get().area()) <= waste)
{
if(item_area <= free_area && placer.pack(*it) ) {
if(item_area <= free_area && placer.pack(not_packed, it) ) {
free_area -= item_area;
filled_area = bin_area - free_area;
ret = true;
@ -278,7 +278,7 @@ public:
if(item_area + smallestPiece(it, not_packed)->get().area() >
free_area ) { it++; continue; }
auto pr = placer.trypack(*it);
auto pr = placer.trypack(not_packed, it);
// First would fit
it2 = not_packed.begin();
@ -294,14 +294,14 @@ public:
}
placer.accept(pr);
auto pr2 = placer.trypack(*it2);
auto pr2 = placer.trypack(not_packed, it2);
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
pr2 = placer.trypack(*it2);
pr2 = placer.trypack(not_packed, it2);
if(pr2) {
placer.accept(pr2);
auto pr12 = placer.trypack(*it);
auto pr12 = placer.trypack(not_packed, it);
if(pr12) {
placer.accept(pr12);
ret = true;
@ -394,7 +394,7 @@ public:
it++; continue;
}
auto pr = placer.trypack(*it);
auto pr = placer.trypack(not_packed, it);
// Check for free area and try to pack the 1st item...
if(!pr) { it++; continue; }
@ -420,15 +420,15 @@ public:
bool can_pack2 = false;
placer.accept(pr);
auto pr2 = placer.trypack(*it2);
auto pr2 = placer.trypack(not_packed, it2);
auto pr12 = pr;
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
pr2 = placer.trypack(*it2);
pr2 = placer.trypack(not_packed, it2);
if(pr2) {
placer.accept(pr2);
pr12 = placer.trypack(*it);
pr12 = placer.trypack(not_packed, it);
if(pr12) can_pack2 = true;
placer.unpackLast();
}
@ -463,7 +463,7 @@ public:
if(a3_sum > free_area) { it3++; continue; }
placer.accept(pr12); placer.accept(pr2);
bool can_pack3 = placer.pack(*it3);
bool can_pack3 = placer.pack(not_packed, it3);
if(!can_pack3) {
placer.unpackLast();
@ -473,16 +473,16 @@ public:
if(!can_pack3 && try_reverse) {
std::array<size_t, 3> indices = {0, 1, 2};
std::array<ItemRef, 3>
candidates = {*it, *it2, *it3};
std::array<typename ItemList::iterator, 3>
candidates = {it, it2, it3};
auto tryPack = [&placer, &candidates](
auto tryPack = [&placer, &candidates, &not_packed](
const decltype(indices)& idx)
{
std::array<bool, 3> packed = {false};
for(auto id : idx) packed.at(id) =
placer.pack(candidates[id]);
placer.pack(not_packed, candidates[id]);
bool check =
std::all_of(packed.begin(),
@ -536,7 +536,7 @@ public:
{ auto it = store_.begin();
while (it != store_.end()) {
Placer p(bin); p.configure(pconfig);
if(!p.pack(*it)) {
if(!p.pack(store_, it)) {
it = store_.erase(it);
} else it++;
}
@ -601,7 +601,7 @@ public:
while(it != not_packed.end() &&
filled_area < INITIAL_FILL_AREA)
{
if(placer.pack(*it)) {
if(placer.pack(not_packed, it)) {
filled_area += it->get().area();
free_area = bin_area - filled_area;
it = not_packed.erase(it);

View File

@ -65,7 +65,7 @@ public:
auto it = store_.begin();
while(it != store_.end()) {
if(!placer.pack(*it)) {
if(!placer.pack(store_, it)) {
if(packed_bins_.back().empty()) ++it;
// makeProgress(placer);
placer.clearItems();

View File

@ -40,6 +40,7 @@ public:
packed_bins_.clear();
std::vector<Placer> placers;
placers.reserve(last-first);
std::copy(first, last, std::back_inserter(store_));
@ -60,18 +61,19 @@ public:
{ auto it = store_.begin();
while (it != store_.end()) {
Placer p(bin); p.configure(pconfig);
if(!p.pack(*it)) {
if(!p.pack(store_, it)) {
it = store_.erase(it);
} else it++;
}
}
for(auto& item : store_ ) {
auto it = store_.begin();
while(it != store_.end()) {
bool was_packed = false;
while(!was_packed) {
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
if((was_packed = placers[j].pack(item)))
if((was_packed = placers[j].pack(store_, it)))
makeProgress(placers[j], j);
}
@ -81,6 +83,7 @@ public:
packed_bins_.emplace_back();
}
}
++it;
}
}

View File

@ -471,8 +471,8 @@ TEST(GeometryAlgorithms, BottomLeftStressTest) {
auto next = it;
int i = 0;
while(it != input.end() && ++next != input.end()) {
placer.pack(*it);
placer.pack(*next);
placer.pack(input, it);
placer.pack(input, next);
auto result = placer.getItems();
bool valid = true;

View File

@ -99,6 +99,7 @@ namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter,
@ -109,24 +110,21 @@ objfunc(const PointImpl& bincenter,
double norm, // A norming factor for physical dimensions
std::vector<double>& areacache, // pile item areas will be cached
// a spatial index to quickly get neighbors of the candidate item
SpatIndex& spatindex
SpatIndex& spatindex,
const ItemGroup& remaining
)
{
using pl = PointLike;
using sl = ShapeLike;
using Coord = TCoord<PointImpl>;
static const double BIG_ITEM_TRESHOLD = 0.02;
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
auto isBig = [&areacache, bin_area](double a) {
double farea = areacache.empty() ? 0 : areacache.front();
bool fbig = farea / bin_area > BIG_ITEM_TRESHOLD;
bool abig = a/bin_area > BIG_ITEM_TRESHOLD;
bool rbig = fbig && a > 0.5*farea;
return abig || rbig;
return a/bin_area > BIG_ITEM_TRESHOLD ;
};
// If a new bin has been created:
@ -195,39 +193,74 @@ objfunc(const PointImpl& bincenter,
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;
double density = 0;
// Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item aligned with
// its neighbors. We will check the aligment 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 = std::numeric_limits<double>::max();
if(remaining.empty()) {
pile.emplace_back(item.transformedShape());
auto chull = sl::convexHull(pile);
pile.pop_back();
strategies::EdgeCache<PolygonImpl> ec(chull);
auto& trsh = item.transformedShape();
double circ = ec.circumference() / norm;
double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
score = 0.5*circ + 0.5*bcirc;
auto querybb = item.boundingBox();
} 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 aligment 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 = std::numeric_limits<double>::max();
// Query the spatial index for the neigbours
std::vector<SpatElement> result;
spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
density = (fullbb.width()*fullbb.height()) / (norm*norm);
auto& trsh = item.transformedShape();
auto querybb = item.boundingBox();
auto wp = querybb.width()*0.2;
auto hp = querybb.height()*0.2;
auto pad = PointImpl( Coord(wp), Coord(hp));
querybb = Box({ querybb.minCorner() - pad,
querybb.maxCorner() + pad
});
for(auto& e : result) { // now get the score for the best alignment
auto idx = e.second;
auto& p = pile[idx];
auto parea = areacache[idx];
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
auto bbarea = bb.area();
auto ascore = 1.0 - (item.area() + parea)/bbarea;
// Query the spatial index for the neigbours
std::vector<SpatElement> result;
result.reserve(spatindex.size());
spatindex.query(bgi::intersects(querybb),
std::back_inserter(result));
// if(result.empty()) {
// std::cout << "Error while arranging!" << std::endl;
// std::cout << spatindex.size() << " " << pile.size() << std::endl;
// auto ib = spatindex.bounds();
// Box ibb;
// boost::geometry::convert(ib, ibb);
// std::cout << "Inside: " << (sl::isInside<PolygonImpl>(querybb, ibb) ||
// boost::geometry::intersects(querybb, ibb)) << std::endl;
// }
for(auto& e : result) { // now get the score for the best alignment
auto idx = e.second;
auto& p = pile[idx];
auto parea = areacache[idx];
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
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 neigbours
if(result.empty())
score = 0.5 * dist + 0.5 * density;
else
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
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 neigbours
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
} else if( !isBig(item.area()) && spatindex.empty()) {
// If there are no big items, only small, we should consider the
// density here as well to not get silly results
@ -312,10 +345,12 @@ public:
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
const ItemGroup& rem) {
auto result = objfunc(bin.center(), bin_area_, pile,
pile_area, item, norm, areacache_, rtree_);
pile_area, item, norm, areacache_,
rtree_,
rem);
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
@ -346,10 +381,11 @@ public:
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
const ItemGroup& rem) {
auto result = objfunc(bin.center(), bin_area_, pile,
pile_area, item, norm, areacache_, rtree_);
pile_area, item, norm, areacache_,
rtree_, rem);
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
@ -391,11 +427,12 @@ public:
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
const ItemGroup& rem) {
auto binbb = ShapeLike::boundingBox(bin);
auto result = objfunc(binbb.center(), bin_area_, pile,
pile_area, item, norm, areacache_, rtree_);
pile_area, item, norm, areacache_,
rtree_, rem);
double score = std::get<0>(result);
return score;
@ -417,10 +454,11 @@ public:
const Item &item,
double pile_area,
double norm,
double /*penality*/) {
const ItemGroup& rem) {
auto result = objfunc({0, 0}, 0, pile, pile_area,
item, norm, areacache_, rtree_);
item, norm, areacache_,
rtree_, rem);
return std::get<0>(result);
};