Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/libslic3r/Arrange.cpp')
-rw-r--r--src/libslic3r/Arrange.cpp430
1 files changed, 189 insertions, 241 deletions
diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp
index b8ef0bcdc..3800d49e3 100644
--- a/src/libslic3r/Arrange.cpp
+++ b/src/libslic3r/Arrange.cpp
@@ -1,12 +1,13 @@
#include "Arrange.hpp"
-#include "Geometry.hpp"
#include "SVG.hpp"
-#include "MTUtils.hpp"
+
+#include "BoundingBox.hpp"
#include <libnest2d/backends/clipper/geometries.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include <libnest2d/selections/firstfit.hpp>
+#include <libnest2d/utils/rotcalipers.hpp>
#include <numeric>
#include <ClipperUtils.hpp>
@@ -52,8 +53,8 @@ template<class S> struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
namespace Slic3r {
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
-inline SLIC3R_CONSTEXPR Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
- const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT
+inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
+ const ClipperLib::IntPoint &v) noexcept
{
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X),
unscaled<Tout>(v.Y)};
@@ -83,7 +84,7 @@ const double BIG_ITEM_TRESHOLD = 0.02;
// Fill in the placer algorithm configuration with values carefully chosen for
// Slic3r.
template<class PConf>
-void fillConfig(PConf& pcfg) {
+void fill_config(PConf& pcfg, const ArrangeParams &params) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
@@ -93,19 +94,23 @@ void fillConfig(PConf& pcfg) {
// TODO cannot use rotations until multiple objects of same geometry can
// handle different rotations.
- pcfg.rotations = { 0.0 };
+ if (params.allow_rotations)
+ pcfg.rotations = {0., PI / 2., PI, 3. * PI / 2. };
+ else
+ pcfg.rotations = {0.};
// The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well
- pcfg.accuracy = 0.65f;
+ pcfg.accuracy = params.accuracy;
// Allow parallel execution.
- pcfg.parallel = true;
+ pcfg.parallel = params.parallel;
}
// Apply penalty to object function result. This is used only when alignment
// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
-double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
+// Also, this will only work well for Box shaped beds.
+static double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
{
double score = std::get<0>(result);
Box pilebb = std::get<1>(result);
@@ -277,10 +282,10 @@ protected:
if (result.empty())
score = 0.50 * dist + 0.50 * density;
else
- score = R * 0.60 * dist +
- (1.0 - R) * 0.20 * density +
- 0.20 * alignment_score;
-
+ // Let the density matter more when fewer objects remain
+ score = 0.50 * dist + (1.0 - R) * 0.20 * density +
+ 0.30 * alignment_score;
+
break;
}
case LAST_BIG_ITEM: {
@@ -304,15 +309,15 @@ protected:
public:
AutoArranger(const TBin & bin,
- Distance dist,
+ const ArrangeParams &params,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond)
- : m_pck(bin, dist)
+ : m_pck(bin, params.min_obj_distance)
, m_bin(bin)
, m_bin_area(sl::area(bin))
, m_norm(std::sqrt(m_bin_area))
{
- fillConfig(m_pconf);
+ fill_config(m_pconf, params);
// Set up a callback that is called just before arranging starts
// This functionality is provided by the Nester class (m_pack).
@@ -343,18 +348,42 @@ public:
};
m_pconf.object_function = get_objfn();
+
+ m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
+ if (items.empty()) return;
+
+ cfg.alignment = PConfig::Alignment::DONT_ALIGN;
+ auto bb = sl::boundingBox(m_bin);
+ auto bbcenter = bb.center();
+ cfg.object_function = [this, bb, bbcenter](const Item &item) {
+ return fixed_overfit(objfunc(item, bbcenter), bb);
+ };
+ };
+
+ auto on_packed = params.on_packed;
- if (progressind) m_pck.progressIndicator(progressind);
+ if (progressind || on_packed)
+ m_pck.progressIndicator([this, progressind, on_packed](unsigned rem) {
+
+ if (progressind)
+ progressind(rem);
+
+ if (on_packed) {
+ int last_bed = m_pck.lastPackedBinId();
+ if (last_bed >= 0) {
+ Item &last_packed = m_pck.lastResult()[last_bed].back();
+ ArrangePolygon ap;
+ ap.bed_idx = last_packed.binId();
+ ap.priority = last_packed.priority();
+ on_packed(ap);
+ }
+ }
+ });
+
if (stopcond) m_pck.stopCondition(stopcond);
m_pck.configure(m_pconf);
}
-
- AutoArranger(const TBin & bin,
- std::function<void(unsigned)> progressind,
- std::function<bool(void)> stopcond)
- : AutoArranger{bin, 0 /* no min distance */, progressind, stopcond}
- {}
template<class It> inline void operator()(It from, It to) {
m_rtree.clear();
@@ -363,22 +392,15 @@ public:
m_item_count = 0;
}
- inline void preload(std::vector<Item>& fixeditems) {
- m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
- auto bb = sl::boundingBox(m_bin);
- auto bbcenter = bb.center();
- m_pconf.object_function = [this, bb, bbcenter](const Item &item) {
- return fixed_overfit(objfunc(item, bbcenter), bb);
- };
-
- // Build the rtree for queries to work
-
+ PConfig& config() { return m_pconf; }
+ const PConfig& config() const { return m_pconf; }
+
+ inline void preload(std::vector<Item>& fixeditems) {
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
Item& itm = fixeditems[idx];
itm.markAsFixedInBin(itm.binId());
}
- m_pck.configure(m_pconf);
m_item_count += fixeditems.size();
}
};
@@ -392,12 +414,12 @@ template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
-
+
double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0? miss : 0;
- score += miss*miss;
+ score += miss * miss;
- return score;
+ return score;
};
}
@@ -438,127 +460,6 @@ std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn()
};
}
-inline Circle to_lnCircle(const CircleBed& circ) {
- return Circle({circ.center()(0), circ.center()(1)}, circ.radius());
-}
-
-// Get the type of bed geometry from a simple vector of points.
-void BedShapeHint::reset(BedShapes type)
-{
- if (m_type != type) {
- if (m_type == bsIrregular)
- m_bed.polygon.Slic3r::Polyline::~Polyline();
- else if (type == bsIrregular)
- ::new (&m_bed.polygon) Polyline();
- }
-
- m_type = type;
-}
-
-BedShapeHint::BedShapeHint(const Polyline &bed) {
- auto x = [](const Point& p) { return p(X); };
- auto y = [](const Point& p) { return p(Y); };
-
- auto width = [x](const BoundingBox& box) {
- return x(box.max) - x(box.min);
- };
-
- auto height = [y](const BoundingBox& box) {
- return y(box.max) - y(box.min);
- };
-
- auto area = [&width, &height](const BoundingBox& box) {
- double w = width(box);
- double h = height(box);
- return w * h;
- };
-
- auto poly_area = [](Polyline p) {
- Polygon pp; pp.points.reserve(p.points.size() + 1);
- pp.points = std::move(p.points);
- pp.points.emplace_back(pp.points.front());
- return std::abs(pp.area());
- };
-
- auto distance_to = [x, y](const Point& p1, const Point& p2) {
- double dx = x(p2) - x(p1);
- double dy = y(p2) - y(p1);
- return std::sqrt(dx*dx + dy*dy);
- };
-
- auto bb = bed.bounding_box();
-
- auto isCircle = [bb, distance_to](const Polyline& polygon) {
- auto center = bb.center();
- std::vector<double> vertex_distances;
- double avg_dist = 0;
- for (auto pt: polygon.points)
- {
- double distance = distance_to(center, pt);
- vertex_distances.push_back(distance);
- avg_dist += distance;
- }
-
- avg_dist /= vertex_distances.size();
-
- CircleBed ret(center, avg_dist);
- for(auto el : vertex_distances)
- {
- if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
- ret = CircleBed();
- break;
- }
- }
-
- return ret;
- };
-
- auto parea = poly_area(bed);
-
- if( (1.0 - parea/area(bb)) < 1e-3 ) {
- m_type = BedShapes::bsBox;
- m_bed.box = bb;
- }
- else if(auto c = isCircle(bed)) {
- m_type = BedShapes::bsCircle;
- m_bed.circ = c;
- } else {
- assert(m_type != BedShapes::bsIrregular);
- m_type = BedShapes::bsIrregular;
- ::new (&m_bed.polygon) Polyline(bed);
- }
-}
-
-BedShapeHint &BedShapeHint::operator=(BedShapeHint &&cpy)
-{
- reset(cpy.m_type);
-
- switch(m_type) {
- case bsBox: m_bed.box = std::move(cpy.m_bed.box); break;
- case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break;
- case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break;
- case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break;
- case bsUnknown: break;
- }
-
- return *this;
-}
-
-BedShapeHint &BedShapeHint::operator=(const BedShapeHint &cpy)
-{
- reset(cpy.m_type);
-
- switch(m_type) {
- case bsBox: m_bed.box = cpy.m_bed.box; break;
- case bsCircle: m_bed.circ = cpy.m_bed.circ; break;
- case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break;
- case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break;
- case bsUnknown: break;
- }
-
- return *this;
-}
-
template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
{
auto it = items.begin();
@@ -567,25 +468,33 @@ template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
++it : it = items.erase(it);
}
+template<class S> Radians min_area_boundingbox_rotation(const S &sh)
+{
+ return minAreaBoundingBox<S, TCompute<S>, boost::rational<LargeInt>>(sh)
+ .angleToX();
+}
+
template<class BinT> // Arrange for arbitrary bin type
void _arrange(
std::vector<Item> & shapes,
std::vector<Item> & excludes,
const BinT & bin,
- coord_t minobjd,
+ const ArrangeParams &params,
std::function<void(unsigned)> progressfn,
std::function<bool()> stopfn)
{
// Integer ceiling the min distance from the bed perimeters
- coord_t md = minobjd;
- md = (md % 2) ? md / 2 + 1 : md / 2;
+ coord_t md = params.min_obj_distance;
+ md = md / 2;
auto corrected_bin = bin;
sl::offset(corrected_bin, md);
+ ArrangeParams mod_params = params;
+ mod_params.min_obj_distance = 0;
+
+ AutoArranger<BinT> arranger{corrected_bin, mod_params, progressfn, stopfn};
- AutoArranger<BinT> arranger{corrected_bin, progressfn, stopfn};
-
- auto infl = coord_t(std::ceil(minobjd / 2.0));
+ auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0));
for (Item& itm : shapes) itm.inflate(infl);
for (Item& itm : excludes) itm.inflate(infl);
@@ -599,48 +508,127 @@ void _arrange(
for (auto &itm : shapes ) inp.emplace_back(itm);
for (auto &itm : excludes) inp.emplace_back(itm);
+ // Use the minimum bounding box rotation as a starting point.
+ // TODO: This only works for convex hull. If we ever switch to concave
+ // polygon nesting, a convex hull needs to be calculated.
+ if (params.allow_rotations)
+ for (auto &itm : shapes)
+ itm.rotation(min_area_boundingbox_rotation(itm.rawShape()));
+
arranger(inp.begin(), inp.end());
for (Item &itm : inp) itm.inflate(-infl);
}
-// The final client function for arrangement. A progress indicator and
-// a stop predicate can be also be passed to control the process.
-void arrange(ArrangePolygons & arrangables,
- const ArrangePolygons & excludes,
- coord_t min_obj_dist,
- const BedShapeHint & bedhint,
- std::function<void(unsigned)> progressind,
- std::function<bool()> stopcondition)
+inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
+inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
+inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); }
+inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
+
+inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
+inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); }
+inline double area(const BoundingBox& box) { return double(width(box)) * height(box); }
+inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); }
+inline double distance_to(const Point& p1, const Point& p2)
{
- namespace clppr = ClipperLib;
+ double dx = p2.x() - p1.x();
+ double dy = p2.y() - p1.y();
+ return std::sqrt(dx*dx + dy*dy);
+}
+
+static CircleBed to_circle(const Point &center, const Points& points) {
+ std::vector<double> vertex_distances;
+ double avg_dist = 0;
- std::vector<Item> items, fixeditems;
- items.reserve(arrangables.size());
+ for (auto pt : points)
+ {
+ double distance = distance_to(center, pt);
+ vertex_distances.push_back(distance);
+ avg_dist += distance;
+ }
+
+ avg_dist /= vertex_distances.size();
- // Create Item from Arrangeable
- auto process_arrangeable = [](const ArrangePolygon &arrpoly,
- std::vector<Item> & outp)
+ CircleBed ret(center, avg_dist);
+ for(auto el : vertex_distances)
{
- Polygon p = arrpoly.poly.contour;
- const Vec2crd &offs = arrpoly.translation;
- double rotation = arrpoly.rotation;
+ if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
+ ret = {};
+ break;
+ }
+ }
+
+ return ret;
+}
- if (p.is_counter_clockwise()) p.reverse();
+// Create Item from Arrangeable
+static void process_arrangeable(const ArrangePolygon &arrpoly,
+ std::vector<Item> & outp)
+{
+ Polygon p = arrpoly.poly.contour;
+ const Vec2crd &offs = arrpoly.translation;
+ double rotation = arrpoly.rotation;
- clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
-
- if (!clpath.Contour.empty()) {
- auto firstp = clpath.Contour.front();
- clpath.Contour.emplace_back(firstp);
- }
+ if (p.is_counter_clockwise()) p.reverse();
- outp.emplace_back(std::move(clpath));
- outp.back().rotation(rotation);
- outp.back().translation({offs.x(), offs.y()});
- outp.back().binId(arrpoly.bed_idx);
- outp.back().priority(arrpoly.priority);
- };
+ clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
+ // This fixes:
+ // https://github.com/prusa3d/PrusaSlicer/issues/2209
+ if (clpath.Contour.size() < 3)
+ return;
+
+ auto firstp = clpath.Contour.front();
+ clpath.Contour.emplace_back(firstp);
+
+ outp.emplace_back(std::move(clpath));
+ outp.back().rotation(rotation);
+ outp.back().translation({offs.x(), offs.y()});
+ outp.back().binId(arrpoly.bed_idx);
+ outp.back().priority(arrpoly.priority);
+}
+
+template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
+{
+ if (bed.empty())
+ return fn(InfiniteBed{});
+ else if (bed.size() == 1)
+ return fn(InfiniteBed{bed.front()});
+ else {
+ auto bb = BoundingBox(bed);
+ CircleBed circ = to_circle(bb.center(), bed);
+ auto parea = poly_area(bed);
+
+ if ((1.0 - parea / area(bb)) < 1e-3)
+ return fn(bb);
+ else if (!std::isnan(circ.radius()))
+ return fn(circ);
+ else
+ return fn(Polygon(bed));
+ }
+}
+
+template<>
+void arrange(ArrangePolygons & items,
+ const ArrangePolygons &excludes,
+ const Points & bed,
+ const ArrangeParams & params)
+{
+ call_with_bed(bed, [&](const auto &bin) {
+ arrange(items, excludes, bin, params);
+ });
+}
+
+template<class BedT>
+void arrange(ArrangePolygons & arrangables,
+ const ArrangePolygons &excludes,
+ const BedT & bed,
+ const ArrangeParams & params)
+{
+ namespace clppr = ClipperLib;
+
+ std::vector<Item> items, fixeditems;
+ items.reserve(arrangables.size());
+
for (ArrangePolygon &arrangeable : arrangables)
process_arrangeable(arrangeable, items);
@@ -649,45 +637,10 @@ void arrange(ArrangePolygons & arrangables,
for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON));
- auto &cfn = stopcondition;
- auto &pri = progressind;
+ auto &cfn = params.stopcondition;
+ auto &pri = params.progressind;
- switch (bedhint.get_type()) {
- case bsBox: {
- // Create the arranger for the box shaped bed
- BoundingBox bbb = bedhint.get_box();
- Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}};
-
- _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn);
- break;
- }
- case bsCircle: {
- auto cc = to_lnCircle(bedhint.get_circle());
-
- _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn);
- break;
- }
- case bsIrregular: {
- auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.get_irregular());
- auto irrbed = sl::create<clppr::Polygon>(std::move(ctour));
- BoundingBox polybb(bedhint.get_irregular());
-
- _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn);
- break;
- }
- case bsInfinite: {
- const InfiniteBed& nobin = bedhint.get_infinite();
- auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()});
-
- _arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn);
- break;
- }
- case bsUnknown: {
- // We know nothing about the bed, let it be infinite and zero centered
- _arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn);
- break;
- }
- }
+ _arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
for(size_t i = 0; i < items.size(); ++i) {
clppr::IntPoint tr = items[i].translation();
@@ -697,15 +650,10 @@ void arrange(ArrangePolygons & arrangables,
}
}
-// Arrange, without the fixed items (excludes)
-void arrange(ArrangePolygons & inp,
- coord_t min_d,
- const BedShapeHint & bedhint,
- std::function<void(unsigned)> prfn,
- std::function<bool()> stopfn)
-{
- arrange(inp, {}, min_d, bedhint, prfn, stopfn);
-}
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
+template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
} // namespace arr
} // namespace Slic3r