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
Vertex translation_;
Radians rotation_;
Coord offset_distance_;
Coord inflation_;
// Info about whether the transformations will have to take place
// 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.
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.
mutable RawShape tr_cache_;
mutable bool tr_cache_valid_ = false;
mutable double area_cache_ = 0;
mutable bool area_cache_valid_ = false;
mutable RawShape offset_cache_;
mutable bool offset_cache_valid_ = false;
mutable RawShape inflate_cache_;
mutable bool inflate_cache_valid_ = false;
enum class Convexity: char {
UNCHECKED,
@ -66,7 +66,9 @@ class _Item {
BBCache(): valid(false) {}
} 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};
public:
@ -126,12 +128,12 @@ public:
THolesContainer<RawShape>&& holes):
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
template<class... Args>
_Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args):
_Item(std::forward<Args>(args)...)
{
applyfn_ = std::move(applyfn);
}
// template<class... Args>
// _Item(std::function<void(const _Item&, unsigned)> applyfn, Args &&... args):
// _Item(std::forward<Args>(args)...)
// {
// applyfn_ = std::move(applyfn);
// }
// Call the apply callback set in constructor. Within the callback, the
// original caller can apply the stored transformation to the original
@ -140,13 +142,15 @@ public:
// client uses a simplified or processed polygon for nesting)
// This callback, if present, will be called for each item after the nesting
// is finished.
inline void callApplyFunction(unsigned binidx) const
{
if (applyfn_) applyfn_(*this, binidx);
}
// inline void callApplyFunction(unsigned binidx) const
// {
// if (applyfn_) applyfn_(*this, binidx);
// }
inline bool isFixed() const noexcept { return 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
@ -224,7 +228,7 @@ public:
double ret ;
if(area_cache_valid_) ret = area_cache_;
else {
ret = sl::area(offsettedShape());
ret = sl::area(infaltedShape());
area_cache_ = ret;
area_cache_valid_ = true;
}
@ -295,17 +299,21 @@ public:
{
rotation(rotation() + rads);
}
inline void addOffset(Coord distance) BP2D_NOEXCEPT
inline void inflation(Coord distance) BP2D_NOEXCEPT
{
offset_distance_ = distance;
has_offset_ = true;
inflation_ = distance;
has_inflation_ = true;
invalidateCache();
}
inline void removeOffset() BP2D_NOEXCEPT {
has_offset_ = false;
invalidateCache();
inline Coord inflation() const BP2D_NOEXCEPT {
return inflation_;
}
inline void inflate(Coord distance) BP2D_NOEXCEPT
{
inflation(inflation() + distance);
}
inline Radians rotation() const BP2D_NOEXCEPT
@ -339,7 +347,7 @@ public:
{
if(tr_cache_valid_) return tr_cache_;
RawShape cpy = offsettedShape();
RawShape cpy = infaltedShape();
if(has_rotation_) sl::rotate(cpy, rotation_);
if(has_translation_) sl::translate(cpy, translation_);
tr_cache_ = cpy; tr_cache_valid_ = true;
@ -360,17 +368,17 @@ public:
inline void resetTransformation() BP2D_NOEXCEPT
{
has_translation_ = false; has_rotation_ = false; has_offset_ = false;
has_translation_ = false; has_rotation_ = false; has_inflation_ = false;
invalidateCache();
}
inline Box boundingBox() const {
if(!bb_cache_.valid) {
if(!has_rotation_)
bb_cache_.bb = sl::boundingBox(offsettedShape());
bb_cache_.bb = sl::boundingBox(infaltedShape());
else {
// TODO make sure this works
auto rotsh = offsettedShape();
auto rotsh = infaltedShape();
sl::rotate(rotsh, rotation_);
bb_cache_.bb = sl::boundingBox(rotsh);
}
@ -419,14 +427,14 @@ public:
private:
inline const RawShape& offsettedShape() const {
if(has_offset_ ) {
if(offset_cache_valid_) return offset_cache_;
inline const RawShape& infaltedShape() const {
if(has_inflation_ ) {
if(inflate_cache_valid_) return inflate_cache_;
offset_cache_ = sh_;
sl::offset(offset_cache_, offset_distance_);
offset_cache_valid_ = true;
return offset_cache_;
inflate_cache_ = sh_;
sl::offset(inflate_cache_, inflation_);
inflate_cache_valid_ = true;
return inflate_cache_;
}
return sh_;
}
@ -436,7 +444,7 @@ private:
tr_cache_valid_ = false;
lmb_valid_ = false; rmt_valid_ = false;
area_cache_valid_ = false;
offset_cache_valid_ = false;
inflate_cache_valid_ = false;
bb_cache_.valid = false;
convexity_ = Convexity::UNCHECKED;
}
@ -758,6 +766,25 @@ public:
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
* input items and outputs the items with the proper transformations to be
@ -769,6 +796,7 @@ class Nester {
TSel selector_;
public:
using Item = typename PlacementStrategy::Item;
using ShapeType = typename Item::ShapeType;
using ItemRef = std::reference_wrapper<Item>;
using TPlacer = PlacementStrategyLike<PlacementStrategy>;
using BinType = typename TPlacer::BinType;
@ -777,6 +805,7 @@ public:
using Coord = TCoord<TPoint<typename Item::ShapeType>>;
using PackGroup = _PackGroup<typename Item::ShapeType>;
using ResultType = PackGroup;
template<class K> using NestResult = _NestResult<ShapeType, K>;
private:
BinType bin_;
@ -835,10 +864,12 @@ public:
* The number of groups in the pack group is the number of bins opened by
* the selection algorithm.
*/
template<class TIterator>
inline PackGroup execute(TIterator from, TIterator to)
template<class It, class Key = size_t>
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.
@ -858,65 +889,74 @@ public:
return selector_.getResult();
}
inline void preload(const PackGroup& pgrp)
{
selector_.preload(pgrp);
}
private:
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
// 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.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
inline const PackGroup& _execute(TIterator from, TIterator to, bool = false)
template<class It> using TVal = remove_cvref_t<typename It::value_type>;
template<class It, class Out>
using ConvertibleOnly =
enable_if_t< std::is_convertible<TVal<It>, TPItem>::value, void>;
template<class It, class Out>
using NotConvertibleOnly =
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();
}
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
inline const PackGroup& _execute(TIterator from, TIterator to, int = false)
{
item_cache_ = {from, to};
__execute(item_cache_.begin(), item_cache_.end());
return lastResult();
}
template<class TIter> inline void __execute(TIter from, TIter to)
{
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
auto offs = min_obj_distance_;
if (item.isFixed()) offs *= 0.99;
{
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);
item.addOffset(static_cast<Coord>(std::ceil(offs/2.0)));
++binidx;
}
return result;
}
template<class It, class Key = size_t>
inline NotConvertibleOnly<It, const NestResult<Key>> _execute(
It from, It to, std::function<Key(It)> keyfn)
{
item_cache_.reserve(to - from);
for(auto it = from; it != to; ++it)
item_cache_.emplace_back(Indexed<typename It::value_type>::get(*it));
return _execute(item_cache_.begin(), item_cache_.end(), keyfn);
}
template<class It> inline void __execute(It from, It to)
{
auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
if(infl > 0) std::for_each(from, to, [this](Item& item) {
item.inflate(infl);
});
selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_);
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
PackGroup _arrange(std::vector<Item> & shapes,
std::vector<Item> & excludes,
const BinT & bin,
coord_t minobjd,
std::function<void(unsigned)> prind,
std::function<bool()> stopfn)
_NestResult<clppr::Polygon> _arrange(
std::vector<Item> & shapes,
std::vector<Item> & excludes,
const BinT & bin,
coord_t minobjd,
std::function<void(unsigned)> prind,
std::function<bool()> 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
// will not do this for us.
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
Item &itm = *it;
for (Item &itm : shapes) {
auto ibb = itm.boundingBox();
auto d = binbb.center() - ibb.center();
itm.translate(d);
if (!arranger.is_colliding(itm)) {
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;
}
}
@ -586,9 +578,7 @@ bool arrange(ArrangeablePtrs & arrangables,
coord_t binwidth = 0;
auto process_arrangeable =
[](const Arrangeable * arrangeable,
std::vector<Item> & outp,
std::function<void(const Item &, unsigned)> applyfn)
[](const Arrangeable *arrangeable, std::vector<Item> &outp)
{
assert(arrangeable);
@ -605,29 +595,16 @@ bool arrange(ArrangeablePtrs & arrangables,
auto firstp = clpath.Contour.front();
clpath.Contour.emplace_back(firstp);
outp.emplace_back(applyfn, std::move(clpath));
outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()});
};
for (Arrangeable *arrangeable : arrangables) {
process_arrangeable(
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 (Arrangeable *arrangeable : arrangables)
process_arrangeable(arrangeable, items);
for (const Arrangeable * fixed: excludes)
process_arrangeable(fixed, fixeditems, nullptr);
process_arrangeable(fixed, fixeditems);
// Integer ceiling the min distance from the bed perimeters
coord_t md = min_obj_dist - SCALED_EPSILON;