mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-13 01:37:53 -06:00
Not handling logical beds in arrange()
This commit is contained in:
parent
9372f1c6ad
commit
df7bb94daf
12 changed files with 256 additions and 272 deletions
|
@ -59,20 +59,20 @@ extern template PackGroup Nester<BottomLeftPlacer, FirstFitSelection>::execute(
|
||||||
template<class Placer = NfpPlacer,
|
template<class Placer = NfpPlacer,
|
||||||
class Selector = FirstFitSelection,
|
class Selector = FirstFitSelection,
|
||||||
class Iterator = std::vector<Item>::iterator>
|
class Iterator = std::vector<Item>::iterator>
|
||||||
PackGroup nest(Iterator from, Iterator to,
|
void nest(Iterator from, Iterator to,
|
||||||
const typename Placer::BinType& bin,
|
const typename Placer::BinType& bin,
|
||||||
Coord dist = 0,
|
Coord dist = 0,
|
||||||
const typename Placer::Config& pconf = {},
|
const typename Placer::Config& pconf = {},
|
||||||
const typename Selector::Config& sconf = {})
|
const typename Selector::Config& sconf = {})
|
||||||
{
|
{
|
||||||
Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
|
Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
|
||||||
return nester.execute(from, to);
|
nester.execute(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Placer = NfpPlacer,
|
template<class Placer = NfpPlacer,
|
||||||
class Selector = FirstFitSelection,
|
class Selector = FirstFitSelection,
|
||||||
class Iterator = std::vector<Item>::iterator>
|
class Iterator = std::vector<Item>::iterator>
|
||||||
PackGroup nest(Iterator from, Iterator to,
|
void nest(Iterator from, Iterator to,
|
||||||
const typename Placer::BinType& bin,
|
const typename Placer::BinType& bin,
|
||||||
ProgressFunction prg,
|
ProgressFunction prg,
|
||||||
StopCondition scond = []() { return false; },
|
StopCondition scond = []() { return false; },
|
||||||
|
@ -83,7 +83,7 @@ PackGroup nest(Iterator from, Iterator to,
|
||||||
Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
|
Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
|
||||||
if(prg) nester.progressIndicator(prg);
|
if(prg) nester.progressIndicator(prg);
|
||||||
if(scond) nester.stopCondition(scond);
|
if(scond) nester.stopCondition(scond);
|
||||||
return nester.execute(from, to);
|
nester.execute(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LIBNEST2D_STATIC
|
#ifdef LIBNEST2D_STATIC
|
||||||
|
@ -91,14 +91,14 @@ PackGroup nest(Iterator from, Iterator to,
|
||||||
extern template class Nester<NfpPlacer, FirstFitSelection>;
|
extern template class Nester<NfpPlacer, FirstFitSelection>;
|
||||||
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
|
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
|
||||||
|
|
||||||
extern template PackGroup nest(std::vector<Item>::iterator from,
|
extern template void nest(std::vector<Item>::iterator from,
|
||||||
std::vector<Item>::iterator to,
|
std::vector<Item>::iterator to,
|
||||||
const Box& bin,
|
const Box& bin,
|
||||||
Coord dist = 0,
|
Coord dist = 0,
|
||||||
const NfpPlacer::Config& pconf,
|
const NfpPlacer::Config& pconf,
|
||||||
const FirstFitSelection::Config& sconf);
|
const FirstFitSelection::Config& sconf);
|
||||||
|
|
||||||
extern template PackGroup nest(std::vector<Item>::iterator from,
|
extern template void nest(std::vector<Item>::iterator from,
|
||||||
std::vector<Item>::iterator to,
|
std::vector<Item>::iterator to,
|
||||||
const Box& bin,
|
const Box& bin,
|
||||||
ProgressFunction prg,
|
ProgressFunction prg,
|
||||||
|
@ -112,20 +112,19 @@ extern template PackGroup nest(std::vector<Item>::iterator from,
|
||||||
template<class Placer = NfpPlacer,
|
template<class Placer = NfpPlacer,
|
||||||
class Selector = FirstFitSelection,
|
class Selector = FirstFitSelection,
|
||||||
class Container = std::vector<Item>>
|
class Container = std::vector<Item>>
|
||||||
PackGroup nest(Container&& cont,
|
void nest(Container&& cont,
|
||||||
const typename Placer::BinType& bin,
|
const typename Placer::BinType& bin,
|
||||||
Coord dist = 0,
|
Coord dist = 0,
|
||||||
const typename Placer::Config& pconf = {},
|
const typename Placer::Config& pconf = {},
|
||||||
const typename Selector::Config& sconf = {})
|
const typename Selector::Config& sconf = {})
|
||||||
{
|
{
|
||||||
return nest<Placer, Selector>(cont.begin(), cont.end(),
|
nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, pconf, sconf);
|
||||||
bin, dist, pconf, sconf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Placer = NfpPlacer,
|
template<class Placer = NfpPlacer,
|
||||||
class Selector = FirstFitSelection,
|
class Selector = FirstFitSelection,
|
||||||
class Container = std::vector<Item>>
|
class Container = std::vector<Item>>
|
||||||
PackGroup nest(Container&& cont,
|
void nest(Container&& cont,
|
||||||
const typename Placer::BinType& bin,
|
const typename Placer::BinType& bin,
|
||||||
ProgressFunction prg,
|
ProgressFunction prg,
|
||||||
StopCondition scond = []() { return false; },
|
StopCondition scond = []() { return false; },
|
||||||
|
@ -133,8 +132,8 @@ PackGroup nest(Container&& cont,
|
||||||
const typename Placer::Config& pconf = {},
|
const typename Placer::Config& pconf = {},
|
||||||
const typename Selector::Config& sconf = {})
|
const typename Selector::Config& sconf = {})
|
||||||
{
|
{
|
||||||
return nest<Placer, Selector>(cont.begin(), cont.end(),
|
nest<Placer, Selector>(cont.begin(), cont.end(), bin, prg, scond, dist,
|
||||||
bin, prg, scond, dist, pconf, sconf);
|
pconf, sconf);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
|
static const constexpr int BIN_ID_UNSET = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief An item to be placed on a bin.
|
* \brief An item to be placed on a bin.
|
||||||
*
|
*
|
||||||
|
@ -34,9 +36,9 @@ class _Item {
|
||||||
RawShape sh_;
|
RawShape sh_;
|
||||||
|
|
||||||
// Transformation data
|
// Transformation data
|
||||||
Vertex translation_;
|
Vertex translation_{0, 0};
|
||||||
Radians rotation_;
|
Radians rotation_{0.0};
|
||||||
Coord inflation_;
|
Coord inflation_{0};
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -66,9 +68,7 @@ class _Item {
|
||||||
BBCache(): valid(false) {}
|
BBCache(): valid(false) {}
|
||||||
} bb_cache_;
|
} bb_cache_;
|
||||||
|
|
||||||
static const size_t ID_UNSET = size_t(-1);
|
int binid_{BIN_ID_UNSET};
|
||||||
|
|
||||||
size_t id_{ID_UNSET};
|
|
||||||
bool fixed_{false};
|
bool fixed_{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -149,8 +149,8 @@ public:
|
||||||
|
|
||||||
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 void binId(int idx) { binid_ = idx; }
|
||||||
inline long id() const noexcept { return id_; }
|
inline int binId() const noexcept { return binid_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert the polygon to string representation. The format depends
|
* @brief Convert the polygon to string representation. The format depends
|
||||||
|
@ -766,25 +766,6 @@ 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
|
||||||
|
@ -805,7 +786,6 @@ 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_;
|
||||||
|
@ -816,9 +796,14 @@ private:
|
||||||
using TPItem = remove_cvref_t<Item>;
|
using TPItem = remove_cvref_t<Item>;
|
||||||
using TSItem = remove_cvref_t<SItem>;
|
using TSItem = remove_cvref_t<SItem>;
|
||||||
|
|
||||||
std::vector<TPItem> item_cache_;
|
|
||||||
StopCondition stopfn_;
|
StopCondition stopfn_;
|
||||||
|
|
||||||
|
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>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -864,12 +849,20 @@ 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 It, class Key = size_t>
|
template<class It>
|
||||||
inline const NestResult<Key> execute(It from, It to,
|
inline ConvertibleOnly<It, void> execute(It from, It to)
|
||||||
std::function<Key(It)> keyfn = nullptr)
|
|
||||||
{
|
{
|
||||||
if (!keyfn) keyfn = [to](It it) { return to - it; };
|
auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
|
||||||
return _execute(from, to, keyfn);
|
if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
|
||||||
|
item.inflate(infl);
|
||||||
|
});
|
||||||
|
|
||||||
|
selector_.template packItems<PlacementStrategy>(
|
||||||
|
from, to, bin_, pconfig_);
|
||||||
|
|
||||||
|
if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
|
||||||
|
item.inflate(-infl);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a progress indicator function object for the selector.
|
/// Set a progress indicator function object for the selector.
|
||||||
|
@ -891,73 +884,31 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
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
|
// This function will be used only if the iterators are pointing to
|
||||||
// a type compatible with the libnets2d::_Item template.
|
// a type compatible with the libnets2d::_Item template.
|
||||||
// This way we can use references to input elements as they will
|
// This way we can use references to input elements as they will
|
||||||
// have to exist for the lifetime of this call.
|
// have to exist for the lifetime of this call.
|
||||||
template<class It, class Key>
|
// template<class It, class Key>
|
||||||
inline ConvertibleOnly<It, const NestResult<Key>> _execute(
|
// inline ConvertibleOnly<It, void> _execute(It from, It to)
|
||||||
It from, It to, std::function<Key(It)> keyfn)
|
// {
|
||||||
{
|
// __execute(from, to);
|
||||||
{
|
// }
|
||||||
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);
|
// 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);
|
||||||
|
// });
|
||||||
|
|
||||||
__execute(from, to, keyfn);
|
// selector_.template packItems<PlacementStrategy>(
|
||||||
|
// from, to, bin_, pconfig_);
|
||||||
|
|
||||||
BinIdx binidx = 0;
|
// if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
|
||||||
for(auto &itmgrp : lastResult()) {
|
// item.inflate(-infl);
|
||||||
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 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.inflate(-infl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -712,6 +712,11 @@ public:
|
||||||
packjob(placers[idx], remaining, idx); idx++;
|
packjob(placers[idx], remaining, idx); idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int binid = 0;
|
||||||
|
for(auto &bin : packed_bins_) {
|
||||||
|
for(Item& itm : bin) itm.binId(binid);
|
||||||
|
binid++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,10 @@ public:
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
while(!was_packed && !cancelled()) {
|
while(!was_packed && !cancelled()) {
|
||||||
for(; j < placers.size() && !was_packed && !cancelled(); j++) {
|
for(; j < placers.size() && !was_packed && !cancelled(); j++) {
|
||||||
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
|
if((was_packed = placers[j].pack(*it, rem(it, store_) ))) {
|
||||||
makeProgress(placers[j], j);
|
it->get().binId(int(j));
|
||||||
|
makeProgress(placers[j], j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!was_packed) {
|
if(!was_packed) {
|
||||||
|
|
|
@ -372,27 +372,34 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
|
||||||
|
|
||||||
Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin);
|
Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin);
|
||||||
|
|
||||||
auto groups = arrange.execute(rects.begin(), rects.end());
|
arrange.execute(rects.begin(), rects.end());
|
||||||
|
|
||||||
ASSERT_EQ(groups.size(), 1u);
|
auto max_group = std::max_element(rects.begin(), rects.end(),
|
||||||
ASSERT_EQ(groups[0].size(), rects.size());
|
[](const Item &i1, const Item &i2) {
|
||||||
|
return i1.binId() < i2.binId();
|
||||||
|
});
|
||||||
|
|
||||||
|
int groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
|
||||||
|
|
||||||
|
ASSERT_EQ(groups, 1u);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) {
|
||||||
|
return itm.binId() != BIN_ID_UNSET;
|
||||||
|
}));
|
||||||
|
|
||||||
// check for no intersections, no containment:
|
// check for no intersections, no containment:
|
||||||
|
|
||||||
for(auto result : groups) {
|
bool valid = true;
|
||||||
bool valid = true;
|
for(Item& r1 : rects) {
|
||||||
for(Item& r1 : result) {
|
for(Item& r2 : rects) {
|
||||||
for(Item& r2 : result) {
|
if(&r1 != &r2 ) {
|
||||||
if(&r1 != &r2 ) {
|
valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
|
||||||
valid = !Item::intersects(r1, r2) || Item::touches(r1, r2);
|
ASSERT_TRUE(valid);
|
||||||
ASSERT_TRUE(valid);
|
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
||||||
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
ASSERT_TRUE(valid);
|
||||||
ASSERT_TRUE(valid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
|
TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
|
||||||
|
@ -433,16 +440,25 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
|
||||||
|
|
||||||
Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin, min_obj_distance);
|
Nester<BottomLeftPlacer, DJDHeuristic> arrange(bin, min_obj_distance);
|
||||||
|
|
||||||
auto groups = arrange.execute(rects.begin(), rects.end());
|
arrange.execute(rects.begin(), rects.end());
|
||||||
|
|
||||||
ASSERT_EQ(groups.size(), 1u);
|
auto max_group = std::max_element(rects.begin(), rects.end(),
|
||||||
ASSERT_EQ(groups[0].size(), rects.size());
|
[](const Item &i1, const Item &i2) {
|
||||||
|
return i1.binId() < i2.binId();
|
||||||
|
});
|
||||||
|
|
||||||
|
size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
|
||||||
|
|
||||||
|
ASSERT_EQ(groups, 1u);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
std::all_of(rects.begin(), rects.end(), [](const Rectangle &itm) {
|
||||||
|
return itm.binId() != BIN_ID_UNSET;
|
||||||
|
}));
|
||||||
|
|
||||||
// check for no intersections, no containment:
|
// check for no intersections, no containment:
|
||||||
auto result = groups[0];
|
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
for(Item& r1 : result) {
|
for(Item& r1 : rects) {
|
||||||
for(Item& r2 : result) {
|
for(Item& r2 : rects) {
|
||||||
if(&r1 != &r2 ) {
|
if(&r1 != &r2 ) {
|
||||||
valid = !Item::intersects(r1, r2);
|
valid = !Item::intersects(r1, r2);
|
||||||
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
||||||
|
@ -555,26 +571,23 @@ TEST(GeometryAlgorithms, convexHull) {
|
||||||
TEST(GeometryAlgorithms, NestTest) {
|
TEST(GeometryAlgorithms, NestTest) {
|
||||||
std::vector<Item> input = prusaParts();
|
std::vector<Item> input = prusaParts();
|
||||||
|
|
||||||
PackGroup result = libnest2d::nest(input,
|
libnest2d::nest(input, Box(250000000, 210000000), [](unsigned cnt) {
|
||||||
Box(250000000, 210000000),
|
std::cout << "parts left: " << cnt << std::endl;
|
||||||
[](unsigned cnt) {
|
});
|
||||||
std::cout
|
|
||||||
<< "parts left: " << cnt
|
|
||||||
<< std::endl;
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_LE(result.size(), 2);
|
auto max_binid_it = std::max_element(input.begin(), input.end(),
|
||||||
|
[](const Item &i1, const Item &i2) {
|
||||||
|
return i1.binId() < i2.binId();
|
||||||
|
});
|
||||||
|
|
||||||
size_t partsum = std::accumulate(result.begin(),
|
size_t bins = max_binid_it == input.end() ? 0 : max_binid_it->binId() + 1;
|
||||||
result.end(),
|
|
||||||
size_t(0),
|
|
||||||
[](size_t s,
|
|
||||||
const decltype(
|
|
||||||
result)::value_type &bin) {
|
|
||||||
return s += bin.size();
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_EQ(input.size(), partsum);
|
ASSERT_EQ(bins, 2u);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
std::all_of(input.begin(), input.end(), [](const Item &itm) {
|
||||||
|
return itm.binId() != BIN_ID_UNSET;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -341,9 +341,9 @@ public:
|
||||||
m_pck.configure(m_pconf);
|
m_pck.configure(m_pconf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args> inline PackGroup operator()(Args&&...args) {
|
template<class...Args> inline void operator()(Args&&...args) {
|
||||||
m_rtree.clear();
|
m_rtree.clear();
|
||||||
return m_pck.execute(std::forward<Args>(args)...);
|
m_pck.execute(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void preload(std::vector<Item>& fixeditems) {
|
inline void preload(std::vector<Item>& fixeditems) {
|
||||||
|
@ -513,7 +513,7 @@ BedShapeHint bedShape(const Polyline &bed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class BinT> // Arrange for arbitrary bin type
|
template<class BinT> // Arrange for arbitrary bin type
|
||||||
_NestResult<clppr::Polygon> _arrange(
|
void _arrange(
|
||||||
std::vector<Item> & shapes,
|
std::vector<Item> & shapes,
|
||||||
std::vector<Item> & excludes,
|
std::vector<Item> & excludes,
|
||||||
const BinT & bin,
|
const BinT & bin,
|
||||||
|
@ -553,40 +553,30 @@ _NestResult<clppr::Polygon> _arrange(
|
||||||
for (auto &itm : shapes ) inp.emplace_back(itm);
|
for (auto &itm : shapes ) inp.emplace_back(itm);
|
||||||
for (auto &itm : excludes) inp.emplace_back(itm);
|
for (auto &itm : excludes) inp.emplace_back(itm);
|
||||||
|
|
||||||
return arranger(inp.begin(), inp.end());
|
arranger(inp.begin(), inp.end());
|
||||||
}
|
|
||||||
|
|
||||||
inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w)
|
|
||||||
{
|
|
||||||
return w + w / 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The final client function for arrangement. A progress indicator and
|
// The final client function for arrangement. A progress indicator and
|
||||||
// a stop predicate can be also be passed to control the process.
|
// a stop predicate can be also be passed to control the process.
|
||||||
bool arrange(ArrangeablePtrs & arrangables,
|
void arrange(ArrangePolygons & arrangables,
|
||||||
const ArrangeablePtrs & excludes,
|
const ArrangePolygons & excludes,
|
||||||
coord_t min_obj_dist,
|
coord_t min_obj_dist,
|
||||||
const BedShapeHint & bedhint,
|
const BedShapeHint & bedhint,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind,
|
||||||
std::function<bool()> stopcondition)
|
std::function<bool()> stopcondition)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
|
||||||
namespace clppr = ClipperLib;
|
namespace clppr = ClipperLib;
|
||||||
|
|
||||||
std::vector<Item> items, fixeditems;
|
std::vector<Item> items, fixeditems;
|
||||||
items.reserve(arrangables.size());
|
items.reserve(arrangables.size());
|
||||||
coord_t binwidth = 0;
|
|
||||||
|
|
||||||
|
// Create Item from Arrangeable
|
||||||
auto process_arrangeable =
|
auto process_arrangeable =
|
||||||
[](const Arrangeable *arrangeable, std::vector<Item> &outp)
|
[](const ArrangePolygon &arrpoly, std::vector<Item> &outp)
|
||||||
{
|
{
|
||||||
assert(arrangeable);
|
Polygon p = arrpoly.poly.contour;
|
||||||
|
const Vec2crd & offs = arrpoly.translation;
|
||||||
auto arrangeitem = arrangeable->get_arrange_polygon();
|
double rotation = arrpoly.rotation;
|
||||||
|
|
||||||
Polygon & p = std::get<0>(arrangeitem);
|
|
||||||
const Vec2crd &offs = std::get<1>(arrangeitem);
|
|
||||||
double rotation = std::get<2>(arrangeitem);
|
|
||||||
|
|
||||||
if (p.is_counter_clockwise()) p.reverse();
|
if (p.is_counter_clockwise()) p.reverse();
|
||||||
|
|
||||||
|
@ -600,10 +590,10 @@ bool arrange(ArrangeablePtrs & arrangables,
|
||||||
outp.back().translation({offs.x(), offs.y()});
|
outp.back().translation({offs.x(), offs.y()});
|
||||||
};
|
};
|
||||||
|
|
||||||
for (Arrangeable *arrangeable : arrangables)
|
for (ArrangePolygon &arrangeable : arrangables)
|
||||||
process_arrangeable(arrangeable, items);
|
process_arrangeable(arrangeable, items);
|
||||||
|
|
||||||
for (const Arrangeable * fixed: excludes)
|
for (const ArrangePolygon &fixed: excludes)
|
||||||
process_arrangeable(fixed, fixeditems);
|
process_arrangeable(fixed, fixeditems);
|
||||||
|
|
||||||
// Integer ceiling the min distance from the bed perimeters
|
// Integer ceiling the min distance from the bed perimeters
|
||||||
|
@ -619,7 +609,6 @@ bool arrange(ArrangeablePtrs & arrangables,
|
||||||
BoundingBox bbb = bedhint.shape.box;
|
BoundingBox bbb = bedhint.shape.box;
|
||||||
bbb.min -= Point{md, md}, bbb.max += Point{md, md};
|
bbb.min -= Point{md, md}, bbb.max += Point{md, md};
|
||||||
Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}};
|
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_dist, pri, cfn);
|
_arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn);
|
||||||
break;
|
break;
|
||||||
|
@ -627,7 +616,6 @@ bool arrange(ArrangeablePtrs & arrangables,
|
||||||
case BedShapeType::CIRCLE: {
|
case BedShapeType::CIRCLE: {
|
||||||
auto c = bedhint.shape.circ;
|
auto c = bedhint.shape.circ;
|
||||||
auto cc = to_lnCircle(c);
|
auto cc = to_lnCircle(c);
|
||||||
binwidth = scaled(c.radius());
|
|
||||||
|
|
||||||
_arrange(items, fixeditems, cc, min_obj_dist, pri, cfn);
|
_arrange(items, fixeditems, cc, min_obj_dist, pri, cfn);
|
||||||
break;
|
break;
|
||||||
|
@ -636,7 +624,6 @@ bool arrange(ArrangeablePtrs & arrangables,
|
||||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon);
|
auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.shape.polygon);
|
||||||
auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
|
auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
|
||||||
BoundingBox polybb(bedhint.shape.polygon);
|
BoundingBox polybb(bedhint.shape.polygon);
|
||||||
binwidth = (polybb.max(X) - polybb.min(X));
|
|
||||||
|
|
||||||
_arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn);
|
_arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn);
|
||||||
break;
|
break;
|
||||||
|
@ -655,19 +642,22 @@ bool arrange(ArrangeablePtrs & arrangables,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(stopcondition && stopcondition()) return false;
|
for(size_t i = 0; i < items.size(); ++i) {
|
||||||
|
clppr::IntPoint tr = items[i].translation();
|
||||||
return ret;
|
arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)};
|
||||||
|
arrangables[i].rotation = items[i].rotation();
|
||||||
|
arrangables[i].bed_idx = items[i].binId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrange, without the fixed items (excludes)
|
// Arrange, without the fixed items (excludes)
|
||||||
bool arrange(ArrangeablePtrs & inp,
|
void arrange(ArrangePolygons & inp,
|
||||||
coord_t min_d,
|
coord_t min_d,
|
||||||
const BedShapeHint & bedhint,
|
const BedShapeHint & bedhint,
|
||||||
std::function<void(unsigned)> prfn,
|
std::function<void(unsigned)> prfn,
|
||||||
std::function<bool()> stopfn)
|
std::function<bool()> stopfn)
|
||||||
{
|
{
|
||||||
return arrange(inp, {}, min_d, bedhint, prfn, stopfn);
|
arrange(inp, {}, min_d, bedhint, prfn, stopfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace arr
|
} // namespace arr
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef MODELARRANGE_HPP
|
#ifndef MODELARRANGE_HPP
|
||||||
#define MODELARRANGE_HPP
|
#define MODELARRANGE_HPP
|
||||||
|
|
||||||
#include "Polygon.hpp"
|
#include "ExPolygon.hpp"
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -37,34 +37,57 @@ enum class BedShapeType {
|
||||||
/// Info about the print bed for the arrange() function.
|
/// Info about the print bed for the arrange() function.
|
||||||
struct BedShapeHint {
|
struct BedShapeHint {
|
||||||
BedShapeType type = BedShapeType::INFINITE;
|
BedShapeType type = BedShapeType::INFINITE;
|
||||||
/*union*/ struct { // I know but who cares... TODO: use variant from cpp17?
|
union BedShape_u { // I know but who cares... TODO: use variant from cpp17?
|
||||||
CircleBed circ;
|
CircleBed circ;
|
||||||
BoundingBox box;
|
BoundingBox box;
|
||||||
Polyline polygon;
|
Polyline polygon;
|
||||||
InfiniteBed infinite;
|
InfiniteBed infinite{};
|
||||||
|
~BedShape_u() {}
|
||||||
|
BedShape_u() {};
|
||||||
} shape;
|
} shape;
|
||||||
|
|
||||||
|
BedShapeHint() {};
|
||||||
|
|
||||||
|
~BedShapeHint() {
|
||||||
|
if (type == BedShapeType::IRREGULAR)
|
||||||
|
shape.polygon.Slic3r::Polyline::~Polyline();
|
||||||
|
};
|
||||||
|
|
||||||
|
BedShapeHint(const BedShapeHint &cpy) {
|
||||||
|
*this = cpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
BedShapeHint& operator=(const BedShapeHint &cpy) {
|
||||||
|
type = cpy.type;
|
||||||
|
switch(type) {
|
||||||
|
case BedShapeType::BOX: shape.box = cpy.shape.box; break;
|
||||||
|
case BedShapeType::CIRCLE: shape.circ = cpy.shape.circ; break;
|
||||||
|
case BedShapeType::IRREGULAR: shape.polygon = cpy.shape.polygon; break;
|
||||||
|
case BedShapeType::INFINITE: shape.infinite = cpy.shape.infinite; break;
|
||||||
|
case BedShapeType::UNKNOWN: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get a bed shape hint for arrange() from a naked Polyline.
|
/// Get a bed shape hint for arrange() from a naked Polyline.
|
||||||
BedShapeHint bedShape(const Polyline& bed);
|
BedShapeHint bedShape(const Polyline& bed);
|
||||||
|
|
||||||
/**
|
static const constexpr long UNARRANGED = -1;
|
||||||
* @brief Classes implementing the Arrangeable interface can be used as input
|
|
||||||
* to the arrange function.
|
|
||||||
*/
|
|
||||||
class Arrangeable {
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Arrangeable() = default;
|
struct ArrangePolygon {
|
||||||
|
const ExPolygon poly;
|
||||||
|
Vec2crd translation{0, 0};
|
||||||
|
double rotation{0.0};
|
||||||
|
long bed_idx{UNARRANGED};
|
||||||
|
|
||||||
/// Apply the result transformation calculated by the arrangement.
|
ArrangePolygon(const ExPolygon &p, const Vec2crd &tr = {}, double rot = 0.0)
|
||||||
virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned bed_num) = 0;
|
: poly{p}, translation{tr}, rotation{rot}
|
||||||
|
{}
|
||||||
/// Get the 2D silhouette to arrange and an initial offset and rotation
|
|
||||||
virtual std::tuple<Polygon, Vec2crd, double> get_arrange_polygon() const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using ArrangeablePtrs = std::vector<Arrangeable*>;
|
using ArrangePolygons = std::vector<ArrangePolygon>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Arranges the model objects on the screen.
|
* \brief Arranges the model objects on the screen.
|
||||||
|
@ -97,20 +120,20 @@ using ArrangeablePtrs = std::vector<Arrangeable*>;
|
||||||
*
|
*
|
||||||
* \param stopcondition A predicate returning true if abort is needed.
|
* \param stopcondition A predicate returning true if abort is needed.
|
||||||
*/
|
*/
|
||||||
bool arrange(ArrangeablePtrs &items,
|
void arrange(ArrangePolygons & items,
|
||||||
coord_t min_obj_distance,
|
coord_t min_obj_distance,
|
||||||
const BedShapeHint& bedhint,
|
const BedShapeHint & bedhint,
|
||||||
std::function<void(unsigned)> progressind = nullptr,
|
std::function<void(unsigned)> progressind = nullptr,
|
||||||
std::function<bool(void)> stopcondition = nullptr);
|
std::function<bool(void)> stopcondition = nullptr);
|
||||||
|
|
||||||
/// Same as the previous, only that it takes unmovable items as an
|
/// Same as the previous, only that it takes unmovable items as an
|
||||||
/// additional argument.
|
/// additional argument.
|
||||||
bool arrange(ArrangeablePtrs &items,
|
void arrange(ArrangePolygons & items,
|
||||||
const ArrangeablePtrs &excludes,
|
const ArrangePolygons & excludes,
|
||||||
coord_t min_obj_distance,
|
coord_t min_obj_distance,
|
||||||
const BedShapeHint& bedhint,
|
const BedShapeHint & bedhint,
|
||||||
std::function<void(unsigned)> progressind = nullptr,
|
std::function<void(unsigned)> progressind = nullptr,
|
||||||
std::function<bool(void)> stopcondition = nullptr);
|
std::function<bool(void)> stopcondition = nullptr);
|
||||||
|
|
||||||
} // arr
|
} // arr
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|
|
@ -404,11 +404,16 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (auto obj : objects) count += obj->instances.size();
|
for (auto obj : objects) count += obj->instances.size();
|
||||||
|
|
||||||
arrangement::ArrangeablePtrs input;
|
arrangement::ArrangePolygons input;
|
||||||
|
ModelInstancePtrs instances;
|
||||||
input.reserve(count);
|
input.reserve(count);
|
||||||
|
instances.reserve(count);
|
||||||
for (ModelObject *mo : objects)
|
for (ModelObject *mo : objects)
|
||||||
for (ModelInstance *minst : mo->instances)
|
for (ModelInstance *minst : mo->instances) {
|
||||||
input.emplace_back(minst);
|
input.emplace_back(minst->get_arrange_polygon());
|
||||||
|
instances.emplace_back(minst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
arrangement::BedShapeHint bedhint;
|
arrangement::BedShapeHint bedhint;
|
||||||
|
|
||||||
|
@ -417,7 +422,22 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||||
bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max));
|
bedhint.shape.box = BoundingBox(scaled(bb->min), scaled(bb->max));
|
||||||
}
|
}
|
||||||
|
|
||||||
return arrangement::arrange(input, scaled(dist), bedhint);
|
arrangement::arrange(input, scaled(dist), bedhint);
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < input.size(); ++i) {
|
||||||
|
auto inst = instances[i];
|
||||||
|
inst->set_rotation(Z, input[i].rotation);
|
||||||
|
auto tr = unscaled<double>(input[i].translation);
|
||||||
|
inst->set_offset(X, tr.x());
|
||||||
|
inst->set_offset(Y, tr.y());
|
||||||
|
|
||||||
|
if (input[i].bed_idx != 0) ret = false; // no logical beds are allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicate the entire model preserving instance relative positions.
|
// Duplicate the entire model preserving instance relative positions.
|
||||||
|
@ -1819,7 +1839,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
|
||||||
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<Polygon, Vec2crd, double> ModelInstance::get_arrange_polygon() const
|
arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
||||||
{
|
{
|
||||||
static const double SIMPLIFY_TOLERANCE_MM = 0.1;
|
static const double SIMPLIFY_TOLERANCE_MM = 0.1;
|
||||||
|
|
||||||
|
@ -1835,15 +1855,15 @@ std::tuple<Polygon, Vec2crd, double> ModelInstance::get_arrange_polygon() const
|
||||||
|
|
||||||
// this may happen for malformed models, see:
|
// this may happen for malformed models, see:
|
||||||
// https://github.com/prusa3d/PrusaSlicer/issues/2209
|
// https://github.com/prusa3d/PrusaSlicer/issues/2209
|
||||||
if (p.points.empty()) return {};
|
if (p.points.empty()) return {{}};
|
||||||
|
|
||||||
Polygons pp{p};
|
Polygons pp{p};
|
||||||
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
|
pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
|
||||||
if (!pp.empty()) p = pp.front();
|
if (!pp.empty()) p = pp.front();
|
||||||
|
|
||||||
return std::make_tuple(p,
|
ExPolygon ep; ep.contour = std::move(p);
|
||||||
Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))},
|
|
||||||
get_rotation(Z));
|
return {ep, Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))}, get_rotation(Z)};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||||
|
|
|
@ -491,7 +491,7 @@ private:
|
||||||
|
|
||||||
// A single instance of a ModelObject.
|
// A single instance of a ModelObject.
|
||||||
// Knows the affine transformation of an object.
|
// Knows the affine transformation of an object.
|
||||||
class ModelInstance : public ModelBase, public arrangement::Arrangeable
|
class ModelInstance : public ModelBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum EPrintVolumeState : unsigned char
|
enum EPrintVolumeState : unsigned char
|
||||||
|
@ -555,19 +555,19 @@ public:
|
||||||
bool is_printable() const { return print_volume_state == PVS_Inside; }
|
bool is_printable() const { return print_volume_state == PVS_Inside; }
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
// Implement arr::Arrangeable interface
|
// Implement arrangement::Arrangeable interface
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Getting the input polygon for arrange
|
// Getting the input polygon for arrange
|
||||||
virtual std::tuple<Polygon, Vec2crd, double> get_arrange_polygon() const override;
|
arrangement::ArrangePolygon get_arrange_polygon() const;
|
||||||
|
|
||||||
// Apply the arrange result on the ModelInstance
|
// Apply the arrange result on the ModelInstance
|
||||||
virtual void apply_arrange_result(Vec2d offs, double rot_rads, unsigned /*bed_num*/) override
|
void apply_arrange_result(Vec2crd offs, double rot_rads)
|
||||||
{
|
{
|
||||||
// write the transformation data into the model instance
|
// write the transformation data into the model instance
|
||||||
set_rotation(Z, rot_rads);
|
set_rotation(Z, rot_rads);
|
||||||
set_offset(X, offs(X));
|
set_offset(X, unscale<double>(offs(X)));
|
||||||
set_offset(Y, offs(Y));
|
set_offset(Y, unscale<double>(offs(Y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -5739,7 +5739,7 @@ const SLAPrint* GLCanvas3D::sla_print() const
|
||||||
return (m_process == nullptr) ? nullptr : m_process->sla_print();
|
return (m_process == nullptr) ? nullptr : m_process->sla_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/)
|
void GLCanvas3D::WipeTowerInfo::apply_arrange_result(Vec2d offset, double rotation_rads)
|
||||||
{
|
{
|
||||||
m_pos = offset;
|
m_pos = offset;
|
||||||
m_rotation = rotation_rads;
|
m_rotation = rotation_rads;
|
||||||
|
|
|
@ -612,7 +612,7 @@ public:
|
||||||
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
|
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
|
||||||
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
|
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
|
||||||
|
|
||||||
class WipeTowerInfo: public arrangement::Arrangeable {
|
class WipeTowerInfo {
|
||||||
Vec2d m_pos = {std::nan(""), std::nan("")};
|
Vec2d m_pos = {std::nan(""), std::nan("")};
|
||||||
Vec2d m_bb_size;
|
Vec2d m_bb_size;
|
||||||
double m_rotation;
|
double m_rotation;
|
||||||
|
@ -624,9 +624,9 @@ public:
|
||||||
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
|
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void apply_arrange_result(Vec2d offset, double rotation_rads, unsigned /*bed_num*/) override;
|
void apply_arrange_result(Vec2d offset, double rotation_rads);
|
||||||
|
|
||||||
virtual std::tuple<Polygon, Vec2crd, double> get_arrange_polygon() const override
|
arrangement::ArrangePolygon get_arrange_polygon() const
|
||||||
{
|
{
|
||||||
Polygon p({
|
Polygon p({
|
||||||
{coord_t(0), coord_t(0)},
|
{coord_t(0), coord_t(0)},
|
||||||
|
@ -636,7 +636,8 @@ public:
|
||||||
{coord_t(0), coord_t(0)},
|
{coord_t(0), coord_t(0)},
|
||||||
});
|
});
|
||||||
|
|
||||||
return std::make_tuple(p, scaled(m_pos), m_rotation);
|
ExPolygon ep; ep.contour = std::move(p);
|
||||||
|
return {ep, scaled(m_pos), m_rotation};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1217,28 +1217,6 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
arrangement::ArrangeablePtrs get_arrange_input(Model &model, const Selection &sel) {
|
|
||||||
auto selmap = sel.get_content();
|
|
||||||
|
|
||||||
size_t count = 0;
|
|
||||||
for (auto obj : model.objects) count += obj->instances.size();
|
|
||||||
|
|
||||||
arrangement::ArrangeablePtrs ret; ret.reserve(count);
|
|
||||||
|
|
||||||
if (selmap.empty())
|
|
||||||
for (ModelObject *mo : model.objects)
|
|
||||||
for (ModelInstance *minst : mo->instances)
|
|
||||||
ret.emplace_back(minst);
|
|
||||||
else
|
|
||||||
for (auto &s : selmap)
|
|
||||||
for (auto &instid : s.second)
|
|
||||||
ret.emplace_back(model.objects[s.first]->instances[instid]);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plater / private
|
// Plater / private
|
||||||
struct Plater::priv
|
struct Plater::priv
|
||||||
{
|
{
|
||||||
|
@ -1447,17 +1425,18 @@ struct Plater::priv
|
||||||
class ArrangeJob : public Job
|
class ArrangeJob : public Job
|
||||||
{
|
{
|
||||||
GLCanvas3D::WipeTowerInfo m_wti;
|
GLCanvas3D::WipeTowerInfo m_wti;
|
||||||
arrangement::ArrangeablePtrs m_selected, m_unselected;
|
arrangement::ArrangePolygons m_selected, m_unselected;
|
||||||
|
|
||||||
static std::array<arrangement::ArrangeablePtrs, 2> collect(
|
static std::array<arrangement::ArrangePolygons, 2> collect(
|
||||||
Model &model, const Selection &sel)
|
Model &model, const Selection &sel)
|
||||||
{
|
{
|
||||||
auto selmap = sel.get_content();
|
const Selection::ObjectIdxsToInstanceIdxsMap &selmap =
|
||||||
|
sel.get_content();
|
||||||
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (auto obj : model.objects) count += obj->instances.size();
|
for (auto obj : model.objects) count += obj->instances.size();
|
||||||
|
|
||||||
arrangement::ArrangeablePtrs selected, unselected;
|
arrangement::ArrangePolygons selected, unselected;
|
||||||
selected.reserve(count + 1 /* for optional wti */);
|
selected.reserve(count + 1 /* for optional wti */);
|
||||||
unselected.reserve(count + 1 /* for optional wti */);
|
unselected.reserve(count + 1 /* for optional wti */);
|
||||||
|
|
||||||
|
@ -1475,12 +1454,12 @@ struct Plater::priv
|
||||||
ModelInstance *inst = model.objects[oidx]
|
ModelInstance *inst = model.objects[oidx]
|
||||||
->instances[iidx];
|
->instances[iidx];
|
||||||
instit == iids.end() ?
|
instit == iids.end() ?
|
||||||
unselected.emplace_back(inst) :
|
unselected.emplace_back(inst->get_arrange_polygon()) :
|
||||||
selected.emplace_back(inst);
|
selected.emplace_back(inst->get_arrange_polygon());
|
||||||
}
|
}
|
||||||
} else // object not selected, all instances are unselected
|
} else // object not selected, all instances are unselected
|
||||||
for (auto inst : model.objects[oidx]->instances)
|
for (auto inst : model.objects[oidx]->instances)
|
||||||
unselected.emplace_back(inst);
|
unselected.emplace_back(inst->get_arrange_polygon());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected.empty()) selected.swap(unselected);
|
if (selected.empty()) selected.swap(unselected);
|
||||||
|
@ -1495,14 +1474,15 @@ struct Plater::priv
|
||||||
m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info();
|
m_wti = plater().view3D->get_canvas3d()->get_wipe_tower_info();
|
||||||
|
|
||||||
const Selection& sel = plater().get_selection();
|
const Selection& sel = plater().get_selection();
|
||||||
|
BoundingBoxf bedbb(plater().bed.get_shape());
|
||||||
auto arrinput = collect(plater().model, sel);
|
auto arrinput = collect(plater().model, sel);
|
||||||
m_selected.swap(arrinput[0]);
|
m_selected.swap(arrinput[0]);
|
||||||
m_unselected.swap(arrinput[1]);
|
m_unselected.swap(arrinput[1]);
|
||||||
|
|
||||||
if (m_wti)
|
if (m_wti)
|
||||||
sel.is_wipe_tower() ?
|
sel.is_wipe_tower() ?
|
||||||
m_selected.emplace_back(&m_wti) :
|
m_selected.emplace_back(m_wti.get_arrange_polygon()) :
|
||||||
m_unselected.emplace_back(&m_wti);
|
m_unselected.emplace_back(m_wti.get_arrange_polygon());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue