Wip in Nester interface

This commit is contained in:
tamasmeszaros 2019-07-04 13:58:18 +02:00
parent e81f8a5fd9
commit 9372f1c6ad
2 changed files with 141 additions and 124 deletions

View file

@ -36,20 +36,20 @@ class _Item {
// Transformation data // Transformation data
Vertex translation_; Vertex translation_;
Radians rotation_; Radians rotation_;
Coord offset_distance_; Coord inflation_;
// Info about whether the transformations will have to take place // Info about whether the transformations will have to take place
// This is needed because if floating point is used, it is hard to say // This is needed because if floating point is used, it is hard to say
// that a zero angle is not a rotation because of testing for equality. // that a zero angle is not a rotation because of testing for equality.
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false; bool has_rotation_ = false, has_translation_ = false, has_inflation_ = false;
// For caching the calculations as they can get pretty expensive. // For caching the calculations as they can get pretty expensive.
mutable RawShape tr_cache_; mutable RawShape tr_cache_;
mutable bool tr_cache_valid_ = false; mutable bool tr_cache_valid_ = false;
mutable double area_cache_ = 0; mutable double area_cache_ = 0;
mutable bool area_cache_valid_ = false; mutable bool area_cache_valid_ = false;
mutable RawShape offset_cache_; mutable RawShape inflate_cache_;
mutable bool offset_cache_valid_ = false; mutable bool inflate_cache_valid_ = false;
enum class Convexity: char { enum class Convexity: char {
UNCHECKED, UNCHECKED,
@ -66,7 +66,9 @@ class _Item {
BBCache(): valid(false) {} BBCache(): valid(false) {}
} bb_cache_; } bb_cache_;
std::function<void(const _Item&, unsigned)> applyfn_; static const size_t ID_UNSET = size_t(-1);
size_t id_{ID_UNSET};
bool fixed_{false}; bool fixed_{false};
public: public:
@ -126,12 +128,12 @@ public:
THolesContainer<RawShape>&& holes): THolesContainer<RawShape>&& holes):
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {} sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
template<class... Args> // template<class... Args>
_Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args): // _Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args):
_Item(std::forward<Args>(args)...) // _Item(std::forward<Args>(args)...)
{ // {
applyfn_ = std::move(applyfn); // applyfn_ = std::move(applyfn);
} // }
// Call the apply callback set in constructor. Within the callback, the // Call the apply callback set in constructor. Within the callback, the
// original caller can apply the stored transformation to the original // original caller can apply the stored transformation to the original
@ -140,13 +142,15 @@ public:
// client uses a simplified or processed polygon for nesting) // client uses a simplified or processed polygon for nesting)
// This callback, if present, will be called for each item after the nesting // This callback, if present, will be called for each item after the nesting
// is finished. // is finished.
inline void callApplyFunction(unsigned binidx) const // inline void callApplyFunction(unsigned binidx) const
{ // {
if (applyfn_) applyfn_(*this, binidx); // if (applyfn_) applyfn_(*this, binidx);
} // }
inline bool isFixed() const noexcept { return fixed_; } inline bool isFixed() const noexcept { return fixed_; }
inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
inline void id(size_t idx) { id_ = idx; }
inline long id() const noexcept { return id_; }
/** /**
* @brief Convert the polygon to string representation. The format depends * @brief Convert the polygon to string representation. The format depends
@ -224,7 +228,7 @@ public:
double ret ; double ret ;
if(area_cache_valid_) ret = area_cache_; if(area_cache_valid_) ret = area_cache_;
else { else {
ret = sl::area(offsettedShape()); ret = sl::area(infaltedShape());
area_cache_ = ret; area_cache_ = ret;
area_cache_valid_ = true; area_cache_valid_ = true;
} }
@ -296,16 +300,20 @@ public:
rotation(rotation() + rads); rotation(rotation() + rads);
} }
inline void addOffset(Coord distance) BP2D_NOEXCEPT inline void inflation(Coord distance) BP2D_NOEXCEPT
{ {
offset_distance_ = distance; inflation_ = distance;
has_offset_ = true; has_inflation_ = true;
invalidateCache(); invalidateCache();
} }
inline void removeOffset() BP2D_NOEXCEPT { inline Coord inflation() const BP2D_NOEXCEPT {
has_offset_ = false; return inflation_;
invalidateCache(); }
inline void inflate(Coord distance) BP2D_NOEXCEPT
{
inflation(inflation() + distance);
} }
inline Radians rotation() const BP2D_NOEXCEPT inline Radians rotation() const BP2D_NOEXCEPT
@ -339,7 +347,7 @@ public:
{ {
if(tr_cache_valid_) return tr_cache_; if(tr_cache_valid_) return tr_cache_;
RawShape cpy = offsettedShape(); RawShape cpy = infaltedShape();
if(has_rotation_) sl::rotate(cpy, rotation_); if(has_rotation_) sl::rotate(cpy, rotation_);
if(has_translation_) sl::translate(cpy, translation_); if(has_translation_) sl::translate(cpy, translation_);
tr_cache_ = cpy; tr_cache_valid_ = true; tr_cache_ = cpy; tr_cache_valid_ = true;
@ -360,17 +368,17 @@ public:
inline void resetTransformation() BP2D_NOEXCEPT inline void resetTransformation() BP2D_NOEXCEPT
{ {
has_translation_ = false; has_rotation_ = false; has_offset_ = false; has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
invalidateCache(); invalidateCache();
} }
inline Box boundingBox() const { inline Box boundingBox() const {
if(!bb_cache_.valid) { if(!bb_cache_.valid) {
if(!has_rotation_) if(!has_rotation_)
bb_cache_.bb = sl::boundingBox(offsettedShape()); bb_cache_.bb = sl::boundingBox(infaltedShape());
else { else {
// TODO make sure this works // TODO make sure this works
auto rotsh = offsettedShape(); auto rotsh = infaltedShape();
sl::rotate(rotsh, rotation_); sl::rotate(rotsh, rotation_);
bb_cache_.bb = sl::boundingBox(rotsh); bb_cache_.bb = sl::boundingBox(rotsh);
} }
@ -419,14 +427,14 @@ public:
private: private:
inline const RawShape& offsettedShape() const { inline const RawShape& infaltedShape() const {
if(has_offset_ ) { if(has_inflation_ ) {
if(offset_cache_valid_) return offset_cache_; if(inflate_cache_valid_) return inflate_cache_;
offset_cache_ = sh_; inflate_cache_ = sh_;
sl::offset(offset_cache_, offset_distance_); sl::offset(inflate_cache_, inflation_);
offset_cache_valid_ = true; inflate_cache_valid_ = true;
return offset_cache_; return inflate_cache_;
} }
return sh_; return sh_;
} }
@ -436,7 +444,7 @@ private:
tr_cache_valid_ = false; tr_cache_valid_ = false;
lmb_valid_ = false; rmt_valid_ = false; lmb_valid_ = false; rmt_valid_ = false;
area_cache_valid_ = false; area_cache_valid_ = false;
offset_cache_valid_ = false; inflate_cache_valid_ = false;
bb_cache_.valid = false; bb_cache_.valid = false;
convexity_ = Convexity::UNCHECKED; convexity_ = Convexity::UNCHECKED;
} }
@ -758,6 +766,25 @@ public:
void clear() { impl_.clear(); } void clear() { impl_.clear(); }
}; };
using BinIdx = unsigned;
template<class S, class Key = size_t> using _NestResult =
std::vector<
std::tuple<Key, // Identifier of the original shape
TPoint<S>, // Translation calculated by nesting
Radians, // Rotation calculated by nesting
BinIdx> // Logical bin index, first is zero
>;
template<class T> struct Indexed {
using ShapeType = T;
static T& get(T& obj) { return obj; }
};
template<class K, class S> struct Indexed<std::pair<K, S>> {
using ShapeType = S;
static S& get(std::pair<K, S>& obj) { return obj.second; }
};
/** /**
* The Arranger is the front-end class for the libnest2d library. It takes the * The Arranger is the front-end class for the libnest2d library. It takes the
* input items and outputs the items with the proper transformations to be * input items and outputs the items with the proper transformations to be
@ -769,6 +796,7 @@ class Nester {
TSel selector_; TSel selector_;
public: public:
using Item = typename PlacementStrategy::Item; using Item = typename PlacementStrategy::Item;
using ShapeType = typename Item::ShapeType;
using ItemRef = std::reference_wrapper<Item>; using ItemRef = std::reference_wrapper<Item>;
using TPlacer = PlacementStrategyLike<PlacementStrategy>; using TPlacer = PlacementStrategyLike<PlacementStrategy>;
using BinType = typename TPlacer::BinType; using BinType = typename TPlacer::BinType;
@ -777,6 +805,7 @@ public:
using Coord = TCoord<TPoint<typename Item::ShapeType>>; using Coord = TCoord<TPoint<typename Item::ShapeType>>;
using PackGroup = _PackGroup<typename Item::ShapeType>; using PackGroup = _PackGroup<typename Item::ShapeType>;
using ResultType = PackGroup; using ResultType = PackGroup;
template<class K> using NestResult = _NestResult<ShapeType, K>;
private: private:
BinType bin_; BinType bin_;
@ -835,10 +864,12 @@ public:
* The number of groups in the pack group is the number of bins opened by * The number of groups in the pack group is the number of bins opened by
* the selection algorithm. * the selection algorithm.
*/ */
template<class TIterator> template<class It, class Key = size_t>
inline PackGroup execute(TIterator from, TIterator to) inline const NestResult<Key> execute(It from, It to,
std::function<Key(It)> keyfn = nullptr)
{ {
return _execute(from, to); if (!keyfn) keyfn = [to](It it) { return to - it; };
return _execute(from, to, keyfn);
} }
/// Set a progress indicator function object for the selector. /// Set a progress indicator function object for the selector.
@ -858,65 +889,74 @@ public:
return selector_.getResult(); return selector_.getResult();
} }
inline void preload(const PackGroup& pgrp)
{
selector_.preload(pgrp);
}
private: private:
template<class TIterator, template<class It> using TVal = remove_cvref_t<typename It::value_type>;
class IT = remove_cvref_t<typename TIterator::value_type>,
// This function will be used only if the iterators are pointing to template<class It, class Out>
// a type compatible with the libnets2d::_Item template. using ConvertibleOnly =
// This way we can use references to input elements as they will enable_if_t< std::is_convertible<TVal<It>, TPItem>::value, void>;
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> template<class It, class Out>
> using NotConvertibleOnly =
inline const PackGroup& _execute(TIterator from, TIterator to, bool = false) enable_if_t< ! std::is_convertible<TVal<It>, TPItem>::value, void>;
// This function will be used only if the iterators are pointing to
// a type compatible with the libnets2d::_Item template.
// This way we can use references to input elements as they will
// have to exist for the lifetime of this call.
template<class It, class Key>
inline ConvertibleOnly<It, const NestResult<Key>> _execute(
It from, It to, std::function<Key(It)> keyfn)
{ {
__execute(from, to); {
return lastResult(); auto it = from; size_t id = 0;
while(it != to)
if (it->id() == Item::ID_UNSET) (it++)->id(id++);
else { id = it->id() + 1; ++it; }
}
NestResult<Key> result(to - from);
__execute(from, to, keyfn);
BinIdx binidx = 0;
for(auto &itmgrp : lastResult()) {
for(const Item& itm : itmgrp)
result[itm.id()] =
std::make_tuple(keyfn(from + itm.id()), itm.translation(),
itm.rotation(), binidx);
++binidx;
}
return result;
} }
template<class TIterator, template<class It, class Key = size_t>
class IT = remove_cvref_t<typename TIterator::value_type>, inline NotConvertibleOnly<It, const NestResult<Key>> _execute(
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT> It from, It to, std::function<Key(It)> keyfn)
>
inline const PackGroup& _execute(TIterator from, TIterator to, int = false)
{ {
item_cache_ = {from, to}; item_cache_.reserve(to - from);
for(auto it = from; it != to; ++it)
item_cache_.emplace_back(Indexed<typename It::value_type>::get(*it));
__execute(item_cache_.begin(), item_cache_.end()); return _execute(item_cache_.begin(), item_cache_.end(), keyfn);
return lastResult();
} }
template<class TIter> inline void __execute(TIter from, TIter to) template<class It> inline void __execute(It from, It to)
{ {
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
auto offs = min_obj_distance_; if(infl > 0) std::for_each(from, to, [this](Item& item) {
if (item.isFixed()) offs *= 0.99; item.inflate(infl);
item.addOffset(static_cast<Coord>(std::ceil(offs/2.0)));
}); });
selector_.template packItems<PlacementStrategy>( selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_); from, to, bin_, pconfig_);
if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) { if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
item.removeOffset(); item.inflate(-infl);
}); });
if(!stopfn_ || (stopfn_ && !stopfn_())) {
// Ignore results if nesting was stopped.
const PackGroup& bins = lastResult();
unsigned binidx = 0;
for(auto& bin : bins) {
for(const Item& itm : bin) itm.callApplyFunction(binidx);
++binidx;
}
}
} }
}; };

View file

@ -513,12 +513,13 @@ BedShapeHint bedShape(const Polyline &bed) {
} }
template<class BinT> // Arrange for arbitrary bin type template<class BinT> // Arrange for arbitrary bin type
PackGroup _arrange(std::vector<Item> & shapes, _NestResult<clppr::Polygon> _arrange(
std::vector<Item> & excludes, std::vector<Item> & shapes,
const BinT & bin, std::vector<Item> & excludes,
coord_t minobjd, const BinT & bin,
std::function<void(unsigned)> prind, coord_t minobjd,
std::function<bool()> stopfn) std::function<void(unsigned)> prind,
std::function<bool()> stopfn)
{ {
AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn}; AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
@ -535,22 +536,13 @@ PackGroup _arrange(std::vector<Item> & shapes,
// Try to put the first item to the center, as the arranger // Try to put the first item to the center, as the arranger
// will not do this for us. // will not do this for us.
for (auto it = shapes.begin(); it != shapes.end(); ++it) { for (Item &itm : shapes) {
Item &itm = *it;
auto ibb = itm.boundingBox(); auto ibb = itm.boundingBox();
auto d = binbb.center() - ibb.center(); auto d = binbb.center() - ibb.center();
itm.translate(d); itm.translate(d);
if (!arranger.is_colliding(itm)) { if (!arranger.is_colliding(itm)) {
itm.markAsFixed(); 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; break;
} }
} }
@ -586,9 +578,7 @@ bool arrange(ArrangeablePtrs & arrangables,
coord_t binwidth = 0; coord_t binwidth = 0;
auto process_arrangeable = auto process_arrangeable =
[](const Arrangeable * arrangeable, [](const Arrangeable *arrangeable, std::vector<Item> &outp)
std::vector<Item> & outp,
std::function<void(const Item &, unsigned)> applyfn)
{ {
assert(arrangeable); assert(arrangeable);
@ -605,29 +595,16 @@ bool arrange(ArrangeablePtrs & arrangables,
auto firstp = clpath.Contour.front(); auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp); clpath.Contour.emplace_back(firstp);
outp.emplace_back(applyfn, std::move(clpath)); outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation); outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()}); outp.back().translation({offs.x(), offs.y()});
}; };
for (Arrangeable *arrangeable : arrangables) { for (Arrangeable *arrangeable : arrangables)
process_arrangeable( process_arrangeable(arrangeable, items);
arrangeable,
items,
// callback called by arrange to apply the result on the arrangeable
[arrangeable, &binwidth, &ret](const Item &itm, unsigned binidx) {
ret = !binidx; // Return value false more bed is required
clppr::cInt stride = binidx * stride_padding(binwidth);
clppr::IntPoint offs = itm.translation();
arrangeable->apply_arrange_result({unscaled(offs.X + stride),
unscaled(offs.Y)},
itm.rotation(), binidx);
});
}
for (const Arrangeable * fixed: excludes) for (const Arrangeable * fixed: excludes)
process_arrangeable(fixed, fixeditems, nullptr); process_arrangeable(fixed, fixeditems);
// Integer ceiling the min distance from the bed perimeters // Integer ceiling the min distance from the bed perimeters
coord_t md = min_obj_dist - SCALED_EPSILON; coord_t md = min_obj_dist - SCALED_EPSILON;