Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrange room for skirt and brim #9181

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 129 additions & 5 deletions src/libnest2d/include/libnest2d/nester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ class _Item {

using VertexConstIterator = typename TContour<RawShape>::const_iterator;

// The original shape that gets encapsulated.
// An original shape that gets encapsulated.
// To be fitted against other _Item's sh_
RawShape sh_;

// An original shape that gets encapsulated.
// To be fitted against the bin edges
RawShape bin_fit_shape_;

// Transformation data
Vertex translation_{0, 0};
Radians rotation_{0.0};
Expand All @@ -48,6 +53,8 @@ class _Item {
// For caching the calculations as they can get pretty expensive.
mutable RawShape tr_cache_;
mutable bool tr_cache_valid_ = false;
mutable RawShape bfs_cache_;
mutable bool bfs_cache_valid_ = false;
mutable double area_cache_ = 0;
mutable bool area_cache_valid_ = false;
mutable RawShape inflate_cache_;
Expand All @@ -67,7 +74,8 @@ class _Item {
Box bb; bool valid;
BBCache(): valid(false) {}
} bb_cache_;

mutable struct BBCache bfs_bb_cache_;

int binid_{BIN_ID_UNSET}, priority_{0};
bool fixed_{false};

Expand Down Expand Up @@ -288,14 +296,41 @@ class _Item {
{
rotation(rotation() + rads);
}

inline void inflation(Coord distance) BP2D_NOEXCEPT
{
inflation_ = distance;
has_inflation_ = true;
invalidateCache();
}


/**
* @brief Setup the shape that is packed against the bed edges.
*
* If this shape is set, the item will be packed against the bed
* borders with this shape. Packing with other items is still
* done with the shape set in the constructor.
*
* The shape contour will be copied into the _Item object.
*
* Note: This shape, if set, is not inflated with the ::inflation
* property.
*
* @param contour Item-to-bin shape
*/
inline void binFitShape(const TContour<RawShape>& contour) BP2D_NOEXCEPT
{
const THolesContainer<RawShape> holes = {};
bin_fit_shape_ = sl::create<RawShape>(contour, holes);
invalidateCache();
}

inline void binFitShape(RawShape&& shape) BP2D_NOEXCEPT
{
bin_fit_shape_ = std::move(shape);
invalidateCache();
}

inline Coord inflation() const BP2D_NOEXCEPT {
return inflation_;
}
Expand All @@ -321,17 +356,24 @@ class _Item {
rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
rmt_valid_ = false; lmb_valid_ = false;
bb_cache_.valid = false;
bfs_bb_cache_.valid = false;
bfs_cache_valid_ = false;
}
}

inline void translation(const TPoint<RawShape>& tr) BP2D_NOEXCEPT
{
if(translation_ != tr) {
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
//bb_cache_.valid = false;
bfs_cache_valid_ = false;
}
}

/**
* @brief The inflated, rotated and translated item shape.
*
* Used to pack items against other items.
*/
inline const RawShape& transformedShape() const
{
if(tr_cache_valid_) return tr_cache_;
Expand All @@ -345,6 +387,34 @@ class _Item {
return tr_cache_;
}

/**
* @brief Returns a rotated and translated shape for fitting
* against the bin borders.
*
* This shape is used to check if the item fits inside the borders
* of the bin.
*
* If a \link binFitShape bin fit shape is set\endlink, this
* returns that shape rotated and translated.
*
* If a bin fit shape is not set, this returns the inflated
* item shape, rotated and translated. It's the same result as
* transformedShape(), for backwards compability.
*/
inline const RawShape& transformedBinFitShape() const
{
if(!bin_fit_shape_.is_valid())
return transformedShape();
if(bfs_cache_valid_) return bfs_cache_;

RawShape cpy = bin_fit_shape_;
if(has_rotation_) sl::rotate(cpy, rotation_);
if(has_translation_) sl::translate(cpy, translation_);
bfs_cache_ = cpy; bfs_cache_valid_ = true;

return bfs_cache_;
}

inline operator RawShape() const
{
return transformedShape();
Expand All @@ -355,6 +425,24 @@ class _Item {
return sh_;
}

/**
* @brief Returns the bin fit shape.
*
* This shape is used for the bin boundary checking.
*
* If a \link binFitShape bin fit shape was setup\endlink, this
* returns that shape. No rotation or translation applied.
*
* If a bin fit shape was not set up, this returns the inflated
* item shape. It's not rotated or translated.
*/
inline const RawShape& untransformedBinFitShape() const BP2D_NOEXCEPT
{
if(bin_fit_shape_.is_valid())
return bin_fit_shape_;
return infaltedShape();
}

inline void resetTransformation() BP2D_NOEXCEPT
{
has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
Expand All @@ -378,6 +466,40 @@ class _Item {
return {bb.minCorner() + tr, bb.maxCorner() + tr };
}

/**
* @brief Returns the bounding box for fitting against the bin
* borders.
*
* This bounding box is used to check if the item fits inside the
* borders of the bin.
*
* If a \link binFitShape bin fit shape was setup\endlink, this
* returns the bounding box of that shape rotated and translated.
*
* If a bin fit shape was not set up, this returns the bounding
* box of the inflated item shape, rotated and translated. It's
* the same result as boundingBox(), for backwards
* compability.
*/
inline Box binFitShapeBoundingBox() const {
if(!bin_fit_shape_.is_valid())
return boundingBox();
if(!bfs_bb_cache_.valid) {
if(!has_rotation_)
bfs_bb_cache_.bb = sl::boundingBox(bin_fit_shape_);
else {
// TODO make sure this works
auto rotsh = bin_fit_shape_;
sl::rotate(rotsh, rotation_);
bfs_bb_cache_.bb = sl::boundingBox(rotsh);
}
bfs_bb_cache_.valid = true;
}

auto &bfs_bb = bfs_bb_cache_.bb; auto &tr = translation_;
return {bfs_bb.minCorner() + tr, bfs_bb.maxCorner() + tr };
}

inline Vertex referenceVertex() const {
return rightmostTopVertex();
}
Expand Down Expand Up @@ -431,10 +553,12 @@ class _Item {
inline void invalidateCache() const BP2D_NOEXCEPT
{
tr_cache_valid_ = false;
bfs_cache_valid_ = false;
lmb_valid_ = false; rmt_valid_ = false;
area_cache_valid_ = false;
inflate_cache_valid_ = false;
bb_cache_.valid = false;
bfs_bb_cache_.valid = false;
convexity_ = Convexity::UNCHECKED;
}

Expand Down
68 changes: 49 additions & 19 deletions src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,19 @@ struct NfpPConfig {
bool parallel = true;

/**
* @brief before_packing Callback that is called just before a search for
* a new item's position is started. You can use this to create various
* cache structures and update them between subsequent packings.
* @brief Callback that is called just before a search for a new
* item's position is started.
*
* \param merged pile A polygon that is the union of all items in the bin.
* You can use this to create various cache structures and update
* them between subsequent packings.
*
* \param merged_pile A polygon that is the union of all items in the bin.
*
* \param merged_bin_fit_pile Similar to merged_pile, this is a
* union of all the items' \link _Item::transformedBinFitShape bin
* fit shapes\endlink. Typically used for checking against the
* border of the bin.
* \see _Item::transformedBinFitShape()
*
* \param pile The items parameter is a container with all the placed
* polygons excluding the current candidate. You can for instance check the
Expand All @@ -112,7 +120,26 @@ struct NfpPConfig {
* into an L shape. This parameter can be used to make these kind of
* decisions (for you or a more intelligent AI).
*/
std::function<void(const nfp::Shapes<RawShape>&, // merged pile
std::function<void(const nfp::Shapes<RawShape>&, // merged pile, item to item shapes
const nfp::Shapes<RawShape>&, // merged pile, bin fit shapes
const ItemGroup&, // packed items
const ItemGroup& // remaining items
)> before_packing_ex;

/**
* @brief This is a simpler version of
* #before_packing_ex without the bin_fit_shape_merged_pile
* parameter.
*
* \param merged_pile A polygon that is the union of all items in the bin.
*
* \param pile The items parameter is a container with all the placed
* polygons excluding the current candidate.
*
* \param remaining A container with the remaining items waiting to be
* placed.
*/
std::function<void(const nfp::Shapes<RawShape>&, // merged pile, item to item shapes
const ItemGroup&, // packed items
const ItemGroup& // remaining items
)> before_packing;
Expand Down Expand Up @@ -440,6 +467,7 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
// Norming factor for the optimization function
const double norm_;
Pile merged_pile_;
Pile merged_bin_fit_pile_;

public:

Expand Down Expand Up @@ -675,14 +703,14 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
setInitialPosition(item);
auto best_tr = item.translation();
auto best_rot = item.rotation();
best_overfit = overfit(item.transformedShape(), bin_);
best_overfit = overfit(item.transformedBinFitShape(), bin_);

for(auto rot : config_.rotations) {
item.translation(initial_tr);
item.rotation(initial_rot + rot);
setInitialPosition(item);
double of = 0.;
if ((of = overfit(item.transformedShape(), bin_)) < best_overfit) {
if ((of = overfit(item.transformedBinFitShape(), bin_)) < best_overfit) {
best_overfit = of;
best_tr = item.translation();
best_rot = item.rotation();
Expand All @@ -694,7 +722,7 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
item.translation(best_tr);
} else {

Pile merged_pile = merged_pile_;
Pile merged_bin_fit_pile = merged_bin_fit_pile_;

for(auto rot : config_.rotations) {

Expand Down Expand Up @@ -737,22 +765,21 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin

auto alignment = config_.alignment;

auto boundaryCheck = [alignment, &merged_pile, &getNfpPoint,
auto boundaryCheck = [alignment, &merged_bin_fit_pile, &getNfpPoint,
&item, &bin, &iv, &startpos] (const Optimum& o)
{
auto v = getNfpPoint(o);
auto d = (v - iv) + startpos;
item.translation(d);

merged_pile.emplace_back(item.transformedShape());
auto chull = sl::convexHull(merged_pile);
merged_pile.pop_back();
merged_bin_fit_pile.emplace_back(item.transformedBinFitShape());
auto chull = sl::convexHull(merged_bin_fit_pile);
merged_bin_fit_pile.pop_back();

double miss = 0;
if(alignment == Config::Alignment::DONT_ALIGN)
miss = sl::isInside(chull, bin) ? -1.0 : 1.0;
miss = sl::isInside(chull, bin) ? -1.0 : 1.0;
else miss = overfit(chull, bin);

return miss;
};

Expand All @@ -762,7 +789,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
if(config_.parallel) policy |= std::launch::async;

if(config_.before_packing)
config_.before_packing(merged_pile, items_, remlist);
config_.before_packing(merged_pile_, items_, remlist);
if(config_.before_packing_ex)
config_.before_packing_ex(merged_pile_, merged_bin_fit_pile_, items_, remlist);

using OptResult = opt::Result<double>;
using OptResults = std::vector<OptResult>;
Expand Down Expand Up @@ -890,6 +919,7 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
if(can_pack) {
ret = PackResult(item);
merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
merged_bin_fit_pile_ = nfp::merge(merged_bin_fit_pile_, item.transformedBinFitShape());
} else {
ret = PackResult(best_overfit);
}
Expand All @@ -908,7 +938,7 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin

nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
for(Item& item : items_) m.emplace_back(item.transformedBinFitShape());

auto c = boundingCircle(sl::convexHull(m));

Expand All @@ -920,9 +950,9 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
if(items_.empty() ||
config_.alignment == Config::Alignment::DONT_ALIGN) return;

Box bb = items_.front().get().boundingBox();
Box bb = items_.front().get().binFitShapeBoundingBox();
for(Item& item : items_)
bb = sl::boundingBox(item.boundingBox(), bb);
bb = sl::boundingBox(item.binFitShapeBoundingBox(), bb);

Vertex ci, cb;

Expand Down Expand Up @@ -999,7 +1029,7 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
}

void placeOutsideOfBin(Item& item) {
auto&& bb = item.boundingBox();
auto&& bb = item.binFitShapeBoundingBox();
Box binbb = sl::boundingBox(bin_);

Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
Expand Down
Loading