Arrange.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. #include "Arrange.hpp"
  2. #include "SVG.hpp"
  3. #include "BoundingBox.hpp"
  4. #include <libnest2d/backends/clipper/geometries.hpp>
  5. #include <libnest2d/optimizers/nlopt/subplex.hpp>
  6. #include <libnest2d/placers/nfpplacer.hpp>
  7. #include <libnest2d/selections/firstfit.hpp>
  8. #include <libnest2d/utils/rotcalipers.hpp>
  9. #include <numeric>
  10. #include <ClipperUtils.hpp>
  11. #include <boost/geometry/index/rtree.hpp>
  12. #if defined(_MSC_VER) && defined(__clang__)
  13. #define BOOST_NO_CXX17_HDR_STRING_VIEW
  14. #endif
  15. #include <boost/multiprecision/integer.hpp>
  16. #include <boost/rational.hpp>
  17. namespace libnest2d {
  18. #if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__)
  19. using LargeInt = __int128;
  20. #else
  21. using LargeInt = boost::multiprecision::int128_t;
  22. template<> struct _NumTag<LargeInt>
  23. {
  24. using Type = ScalarTag;
  25. };
  26. #endif
  27. template<class T> struct _NumTag<boost::rational<T>>
  28. {
  29. using Type = RationalTag;
  30. };
  31. namespace nfp {
  32. template<class S> struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
  33. {
  34. NfpResult<S> operator()(const S &sh, const S &other)
  35. {
  36. return nfpConvexOnly<S, boost::rational<LargeInt>>(sh, other);
  37. }
  38. };
  39. } // namespace nfp
  40. } // namespace libnest2d
  41. namespace Slic3r {
  42. template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
  43. inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
  44. const ClipperLib::IntPoint &v) noexcept
  45. {
  46. return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X),
  47. unscaled<Tout>(v.Y)};
  48. }
  49. namespace arrangement {
  50. using namespace libnest2d;
  51. namespace clppr = ClipperLib;
  52. // Get the libnest2d types for clipper backend
  53. using Item = _Item<clppr::Polygon>;
  54. using Box = _Box<clppr::IntPoint>;
  55. using Circle = _Circle<clppr::IntPoint>;
  56. using Segment = _Segment<clppr::IntPoint>;
  57. using MultiPolygon = TMultiShape<clppr::Polygon>;
  58. // Summon the spatial indexing facilities from boost
  59. namespace bgi = boost::geometry::index;
  60. using SpatElement = std::pair<Box, unsigned>;
  61. using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
  62. using ItemGroup = std::vector<std::reference_wrapper<Item>>;
  63. // A coefficient used in separating bigger items and smaller items.
  64. const double BIG_ITEM_TRESHOLD = 0.02;
  65. // Fill in the placer algorithm configuration with values carefully chosen for
  66. // Slic3r.
  67. template<class PConf>
  68. void fill_config(PConf& pcfg, const ArrangeParams &params) {
  69. // Align the arranged pile into the center of the bin
  70. pcfg.alignment = PConf::Alignment::CENTER;
  71. // Start placing the items from the center of the print bed
  72. pcfg.starting_point = PConf::Alignment::CENTER;
  73. // TODO cannot use rotations until multiple objects of same geometry can
  74. // handle different rotations.
  75. if (params.allow_rotations)
  76. pcfg.rotations = {0., PI / 2., PI, 3. * PI / 2. };
  77. else
  78. pcfg.rotations = {0.};
  79. // The accuracy of optimization.
  80. // Goes from 0.0 to 1.0 and scales performance as well
  81. pcfg.accuracy = params.accuracy;
  82. // Allow parallel execution.
  83. pcfg.parallel = params.parallel;
  84. }
  85. // Apply penalty to object function result. This is used only when alignment
  86. // after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
  87. // Also, this will only work well for Box shaped beds.
  88. static double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
  89. {
  90. double score = std::get<0>(result);
  91. Box pilebb = std::get<1>(result);
  92. Box fullbb = sl::boundingBox(pilebb, binbb);
  93. auto diff = double(fullbb.area()) - binbb.area();
  94. if(diff > 0) score += diff;
  95. return score;
  96. }
  97. // A class encapsulating the libnest2d Nester class and extending it with other
  98. // management and spatial index structures for acceleration.
  99. template<class TBin>
  100. class AutoArranger {
  101. public:
  102. // Useful type shortcuts...
  103. using Placer = typename placers::_NofitPolyPlacer<clppr::Polygon, TBin>;
  104. using Selector = selections::_FirstFitSelection<clppr::Polygon>;
  105. using Packer = _Nester<Placer, Selector>;
  106. using PConfig = typename Packer::PlacementConfig;
  107. using Distance = TCoord<PointImpl>;
  108. protected:
  109. Packer m_pck;
  110. PConfig m_pconf; // Placement configuration
  111. TBin m_bin;
  112. double m_bin_area;
  113. #ifdef _MSC_VER
  114. #pragma warning(push)
  115. #pragma warning(disable: 4244)
  116. #pragma warning(disable: 4267)
  117. #endif
  118. SpatIndex m_rtree; // spatial index for the normal (bigger) objects
  119. SpatIndex m_smallsrtree; // spatial index for only the smaller items
  120. #ifdef _MSC_VER
  121. #pragma warning(pop)
  122. #endif
  123. double m_norm; // A coefficient to scale distances
  124. MultiPolygon m_merged_pile; // The already merged pile (vector of items)
  125. Box m_pilebb; // The bounding box of the merged pile.
  126. ItemGroup m_remaining; // Remaining items
  127. ItemGroup m_items; // allready packed items
  128. size_t m_item_count = 0; // Number of all items to be packed
  129. template<class T> ArithmeticOnly<T, double> norm(T val)
  130. {
  131. return double(val) / m_norm;
  132. }
  133. // This is "the" object function which is evaluated many times for each
  134. // vertex (decimated with the accuracy parameter) of each object.
  135. // Therefore it is upmost crucial for this function to be as efficient
  136. // as it possibly can be but at the same time, it has to provide
  137. // reasonable results.
  138. std::tuple<double /*score*/, Box /*farthest point from bin center*/>
  139. objfunc(const Item &item, const clppr::IntPoint &bincenter)
  140. {
  141. const double bin_area = m_bin_area;
  142. const SpatIndex& spatindex = m_rtree;
  143. const SpatIndex& smalls_spatindex = m_smallsrtree;
  144. // We will treat big items (compared to the print bed) differently
  145. auto isBig = [bin_area](double a) {
  146. return a/bin_area > BIG_ITEM_TRESHOLD ;
  147. };
  148. // Candidate item bounding box
  149. auto ibb = item.boundingBox();
  150. // Calculate the full bounding box of the pile with the candidate item
  151. auto fullbb = sl::boundingBox(m_pilebb, ibb);
  152. // The bounding box of the big items (they will accumulate in the center
  153. // of the pile
  154. Box bigbb;
  155. if(spatindex.empty()) bigbb = fullbb;
  156. else {
  157. auto boostbb = spatindex.bounds();
  158. boost::geometry::convert(boostbb, bigbb);
  159. }
  160. // Will hold the resulting score
  161. double score = 0;
  162. // Density is the pack density: how big is the arranged pile
  163. double density = 0;
  164. // Distinction of cases for the arrangement scene
  165. enum e_cases {
  166. // This branch is for big items in a mixed (big and small) scene
  167. // OR for all items in a small-only scene.
  168. BIG_ITEM,
  169. // This branch is for the last big item in a mixed scene
  170. LAST_BIG_ITEM,
  171. // For small items in a mixed scene.
  172. SMALL_ITEM
  173. } compute_case;
  174. bool bigitems = isBig(item.area()) || spatindex.empty();
  175. if(bigitems && !m_remaining.empty()) compute_case = BIG_ITEM;
  176. else if (bigitems && m_remaining.empty()) compute_case = LAST_BIG_ITEM;
  177. else compute_case = SMALL_ITEM;
  178. switch (compute_case) {
  179. case BIG_ITEM: {
  180. const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner
  181. const clppr::IntPoint& maxc = ibb.maxCorner(); // top right corner
  182. // top left and bottom right corners
  183. clppr::IntPoint top_left{getX(minc), getY(maxc)};
  184. clppr::IntPoint bottom_right{getX(maxc), getY(minc)};
  185. // Now the distance of the gravity center will be calculated to the
  186. // five anchor points and the smallest will be chosen.
  187. std::array<double, 5> dists;
  188. auto cc = fullbb.center(); // The gravity center
  189. dists[0] = pl::distance(minc, cc);
  190. dists[1] = pl::distance(maxc, cc);
  191. dists[2] = pl::distance(ibb.center(), cc);
  192. dists[3] = pl::distance(top_left, cc);
  193. dists[4] = pl::distance(bottom_right, cc);
  194. // The smalles distance from the arranged pile center:
  195. double dist = norm(*(std::min_element(dists.begin(), dists.end())));
  196. double bindist = norm(pl::distance(ibb.center(), bincenter));
  197. dist = 0.8 * dist + 0.2 * bindist;
  198. // Prepare a variable for the alignment score.
  199. // This will indicate: how well is the candidate item
  200. // aligned with its neighbors. We will check the alignment
  201. // with all neighbors and return the score for the best
  202. // alignment. So it is enough for the candidate to be
  203. // aligned with only one item.
  204. auto alignment_score = 1.0;
  205. auto query = bgi::intersects(ibb);
  206. auto& index = isBig(item.area()) ? spatindex : smalls_spatindex;
  207. // Query the spatial index for the neighbors
  208. std::vector<SpatElement> result;
  209. result.reserve(index.size());
  210. index.query(query, std::back_inserter(result));
  211. // now get the score for the best alignment
  212. for(auto& e : result) {
  213. auto idx = e.second;
  214. Item& p = m_items[idx];
  215. auto parea = p.area();
  216. if(std::abs(1.0 - parea/item.area()) < 1e-6) {
  217. auto bb = sl::boundingBox(p.boundingBox(), ibb);
  218. auto bbarea = bb.area();
  219. auto ascore = 1.0 - (item.area() + parea)/bbarea;
  220. if(ascore < alignment_score) alignment_score = ascore;
  221. }
  222. }
  223. density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height()));
  224. double R = double(m_remaining.size()) / m_item_count;
  225. // The final mix of the score is the balance between the
  226. // distance from the full pile center, the pack density and
  227. // the alignment with the neighbors
  228. if (result.empty())
  229. score = 0.50 * dist + 0.50 * density;
  230. else
  231. // Let the density matter more when fewer objects remain
  232. score = 0.50 * dist + (1.0 - R) * 0.20 * density +
  233. 0.30 * alignment_score;
  234. break;
  235. }
  236. case LAST_BIG_ITEM: {
  237. score = norm(pl::distance(ibb.center(), m_pilebb.center()));
  238. break;
  239. }
  240. case SMALL_ITEM: {
  241. // Here there are the small items that should be placed around the
  242. // already processed bigger items.
  243. // No need to play around with the anchor points, the center will be
  244. // just fine for small items
  245. score = norm(pl::distance(ibb.center(), bigbb.center()));
  246. break;
  247. }
  248. }
  249. return std::make_tuple(score, fullbb);
  250. }
  251. std::function<double(const Item&)> get_objfn();
  252. public:
  253. AutoArranger(const TBin & bin,
  254. const ArrangeParams &params,
  255. std::function<void(unsigned)> progressind,
  256. std::function<bool(void)> stopcond)
  257. : m_pck(bin, params.min_obj_distance)
  258. , m_bin(bin)
  259. , m_bin_area(sl::area(bin))
  260. , m_norm(std::sqrt(m_bin_area))
  261. {
  262. fill_config(m_pconf, params);
  263. // Set up a callback that is called just before arranging starts
  264. // This functionality is provided by the Nester class (m_pack).
  265. m_pconf.before_packing =
  266. [this](const MultiPolygon& merged_pile, // merged pile
  267. const ItemGroup& items, // packed items
  268. const ItemGroup& remaining) // future items to be packed
  269. {
  270. m_items = items;
  271. m_merged_pile = merged_pile;
  272. m_remaining = remaining;
  273. m_pilebb = sl::boundingBox(merged_pile);
  274. m_rtree.clear();
  275. m_smallsrtree.clear();
  276. // We will treat big items (compared to the print bed) differently
  277. auto isBig = [this](double a) {
  278. return a / m_bin_area > BIG_ITEM_TRESHOLD ;
  279. };
  280. for(unsigned idx = 0; idx < items.size(); ++idx) {
  281. Item& itm = items[idx];
  282. if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx});
  283. m_smallsrtree.insert({itm.boundingBox(), idx});
  284. }
  285. };
  286. m_pconf.object_function = get_objfn();
  287. m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
  288. if (items.empty()) return;
  289. cfg.alignment = PConfig::Alignment::DONT_ALIGN;
  290. auto bb = sl::boundingBox(m_bin);
  291. auto bbcenter = bb.center();
  292. cfg.object_function = [this, bb, bbcenter](const Item &item) {
  293. return fixed_overfit(objfunc(item, bbcenter), bb);
  294. };
  295. };
  296. auto on_packed = params.on_packed;
  297. if (progressind || on_packed)
  298. m_pck.progressIndicator([this, progressind, on_packed](unsigned rem) {
  299. if (progressind)
  300. progressind(rem);
  301. if (on_packed) {
  302. int last_bed = m_pck.lastPackedBinId();
  303. if (last_bed >= 0) {
  304. Item &last_packed = m_pck.lastResult()[last_bed].back();
  305. ArrangePolygon ap;
  306. ap.bed_idx = last_packed.binId();
  307. ap.priority = last_packed.priority();
  308. on_packed(ap);
  309. }
  310. }
  311. });
  312. if (stopcond) m_pck.stopCondition(stopcond);
  313. m_pck.configure(m_pconf);
  314. }
  315. template<class It> inline void operator()(It from, It to) {
  316. m_rtree.clear();
  317. m_item_count += size_t(to - from);
  318. m_pck.execute(from, to);
  319. m_item_count = 0;
  320. }
  321. PConfig& config() { return m_pconf; }
  322. const PConfig& config() const { return m_pconf; }
  323. inline void preload(std::vector<Item>& fixeditems) {
  324. for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
  325. Item& itm = fixeditems[idx];
  326. itm.markAsFixedInBin(itm.binId());
  327. }
  328. m_item_count += fixeditems.size();
  329. }
  330. };
  331. template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
  332. {
  333. auto bincenter = m_bin.center();
  334. return [this, bincenter](const Item &itm) {
  335. auto result = objfunc(itm, bincenter);
  336. double score = std::get<0>(result);
  337. auto& fullbb = std::get<1>(result);
  338. double miss = Placer::overfit(fullbb, m_bin);
  339. miss = miss > 0? miss : 0;
  340. score += miss * miss;
  341. return score;
  342. };
  343. }
  344. template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
  345. {
  346. auto bincenter = m_bin.center();
  347. return [this, bincenter](const Item &item) {
  348. auto result = objfunc(item, bincenter);
  349. double score = std::get<0>(result);
  350. auto isBig = [this](const Item& itm) {
  351. return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ;
  352. };
  353. if(isBig(item)) {
  354. auto mp = m_merged_pile;
  355. mp.push_back(item.transformedShape());
  356. auto chull = sl::convexHull(mp);
  357. double miss = Placer::overfit(chull, m_bin);
  358. if(miss < 0) miss = 0;
  359. score += miss*miss;
  360. }
  361. return score;
  362. };
  363. }
  364. // Specialization for a generalized polygon.
  365. // Warning: this is unfinished business. It may or may not work.
  366. template<>
  367. std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn()
  368. {
  369. auto bincenter = sl::boundingBox(m_bin).center();
  370. return [this, bincenter](const Item &item) {
  371. return std::get<0>(objfunc(item, bincenter));
  372. };
  373. }
  374. template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
  375. {
  376. auto it = items.begin();
  377. while (it != items.end())
  378. sl::isInside(it->transformedShape(), bin) ?
  379. ++it : it = items.erase(it);
  380. }
  381. template<class S> Radians min_area_boundingbox_rotation(const S &sh)
  382. {
  383. return minAreaBoundingBox<S, TCompute<S>, boost::rational<LargeInt>>(sh)
  384. .angleToX();
  385. }
  386. template<class BinT> // Arrange for arbitrary bin type
  387. void _arrange(
  388. std::vector<Item> & shapes,
  389. std::vector<Item> & excludes,
  390. const BinT & bin,
  391. const ArrangeParams &params,
  392. std::function<void(unsigned)> progressfn,
  393. std::function<bool()> stopfn)
  394. {
  395. // Integer ceiling the min distance from the bed perimeters
  396. coord_t md = params.min_obj_distance;
  397. md = md / 2;
  398. auto corrected_bin = bin;
  399. sl::offset(corrected_bin, md);
  400. ArrangeParams mod_params = params;
  401. mod_params.min_obj_distance = 0;
  402. AutoArranger<BinT> arranger{corrected_bin, mod_params, progressfn, stopfn};
  403. auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0));
  404. for (Item& itm : shapes) itm.inflate(infl);
  405. for (Item& itm : excludes) itm.inflate(infl);
  406. remove_large_items(excludes, corrected_bin);
  407. // If there is something on the plate
  408. if (!excludes.empty()) arranger.preload(excludes);
  409. std::vector<std::reference_wrapper<Item>> inp;
  410. inp.reserve(shapes.size() + excludes.size());
  411. for (auto &itm : shapes ) inp.emplace_back(itm);
  412. for (auto &itm : excludes) inp.emplace_back(itm);
  413. // Use the minimum bounding box rotation as a starting point.
  414. // TODO: This only works for convex hull. If we ever switch to concave
  415. // polygon nesting, a convex hull needs to be calculated.
  416. if (params.allow_rotations)
  417. for (auto &itm : shapes)
  418. itm.rotation(min_area_boundingbox_rotation(itm.rawShape()));
  419. arranger(inp.begin(), inp.end());
  420. for (Item &itm : inp) itm.inflate(-infl);
  421. }
  422. inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
  423. inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
  424. inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); }
  425. inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
  426. inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
  427. inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); }
  428. inline double area(const BoundingBox& box) { return double(width(box)) * height(box); }
  429. inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); }
  430. inline coordf_t distance_to(const Point& p1, const Point& p2)
  431. {
  432. coordf_t dx = coordf_t(p2.x() - p1.x());
  433. coordf_t dy = coordf_t(p2.y() - p1.y());
  434. return std::sqrt(dx*dx + dy*dy);
  435. }
  436. static CircleBed to_circle(const Point &center, const Points& points) {
  437. std::vector<double> vertex_distances;
  438. double avg_dist = 0;
  439. for (auto pt : points)
  440. {
  441. double distance = distance_to(center, pt);
  442. vertex_distances.push_back(distance);
  443. avg_dist += distance;
  444. }
  445. avg_dist /= vertex_distances.size();
  446. CircleBed ret(center, avg_dist);
  447. for(auto el : vertex_distances)
  448. {
  449. if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
  450. ret = {};
  451. break;
  452. }
  453. }
  454. return ret;
  455. }
  456. // Create Item from Arrangeable
  457. static void process_arrangeable(const ArrangePolygon &arrpoly,
  458. std::vector<Item> & outp)
  459. {
  460. Polygon p = arrpoly.poly.contour;
  461. const Vec2crd &offs = arrpoly.translation;
  462. double rotation = arrpoly.rotation;
  463. if (p.is_counter_clockwise()) p.reverse();
  464. clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
  465. // This fixes:
  466. // https://github.com/prusa3d/PrusaSlicer/issues/2209
  467. if (clpath.Contour.size() < 3)
  468. return;
  469. auto firstp = clpath.Contour.front();
  470. clpath.Contour.emplace_back(firstp);
  471. outp.emplace_back(std::move(clpath));
  472. outp.back().rotation(rotation);
  473. outp.back().translation({offs.x(), offs.y()});
  474. outp.back().binId(arrpoly.bed_idx);
  475. outp.back().priority(arrpoly.priority);
  476. }
  477. template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
  478. {
  479. if (bed.empty())
  480. return fn(InfiniteBed{});
  481. else if (bed.size() == 1)
  482. return fn(InfiniteBed{bed.front()});
  483. else {
  484. auto bb = BoundingBox(bed);
  485. CircleBed circ = to_circle(bb.center(), bed);
  486. auto parea = poly_area(bed);
  487. if ((1.0 - parea / area(bb)) < 1e-3)
  488. return fn(bb);
  489. else if (!std::isnan(circ.radius()))
  490. return fn(circ);
  491. else
  492. return fn(Polygon(bed));
  493. }
  494. }
  495. template<>
  496. void arrange(ArrangePolygons & items,
  497. const ArrangePolygons &excludes,
  498. const Points & bed,
  499. const ArrangeParams & params)
  500. {
  501. call_with_bed(bed, [&](const auto &bin) {
  502. arrange(items, excludes, bin, params);
  503. });
  504. }
  505. template<class BedT>
  506. void arrange(ArrangePolygons & arrangables,
  507. const ArrangePolygons &excludes,
  508. const BedT & bed,
  509. const ArrangeParams & params)
  510. {
  511. namespace clppr = ClipperLib;
  512. std::vector<Item> items, fixeditems;
  513. items.reserve(arrangables.size());
  514. for (ArrangePolygon &arrangeable : arrangables)
  515. process_arrangeable(arrangeable, items);
  516. for (const ArrangePolygon &fixed: excludes)
  517. process_arrangeable(fixed, fixeditems);
  518. for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON));
  519. auto &cfn = params.stopcondition;
  520. auto &pri = params.progressind;
  521. _arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
  522. for(size_t i = 0; i < items.size(); ++i) {
  523. clppr::IntPoint tr = items[i].translation();
  524. arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)};
  525. arrangables[i].rotation = items[i].rotation();
  526. arrangables[i].bed_idx = items[i].binId();
  527. }
  528. }
  529. template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams &params);
  530. template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams &params);
  531. template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
  532. template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
  533. } // namespace arr
  534. } // namespace Slic3r