Small objects can now fit inside free space surrounded by objects.
This commit is contained in:
parent
1a6fdb668f
commit
ed0f073ef3
@ -519,20 +519,28 @@ void arrangeRectangles() {
|
|||||||
|
|
||||||
std::vector<Item> proba = {
|
std::vector<Item> proba = {
|
||||||
{
|
{
|
||||||
{ {0, 0}, {20, 20}, {40, 0}, {0, 0} }
|
Rectangle(100, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
{ {0, 100}, {50, 60}, {100, 100}, {50, 0}, {0, 100} }
|
Rectangle(100, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rectangle(100, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rectangle(10, 10)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
proba[0].rotate(Pi/3);
|
||||||
|
proba[1].rotate(Pi-Pi/3);
|
||||||
|
|
||||||
std::vector<Item> input;
|
std::vector<Item> input;
|
||||||
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
||||||
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
||||||
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||||
// input.insert(input.end(), rects.begin(), rects.end());
|
// input.insert(input.end(), rects.begin(), rects.end());
|
||||||
// input.insert(input.end(), proba.begin(), proba.end());
|
input.insert(input.end(), proba.begin(), proba.end());
|
||||||
// input.insert(input.end(), crasher.begin(), crasher.end());
|
// input.insert(input.end(), crasher.begin(), crasher.end());
|
||||||
|
|
||||||
Box bin(250*SCALE, 210*SCALE);
|
Box bin(250*SCALE, 210*SCALE);
|
||||||
@ -569,9 +577,9 @@ void arrangeRectangles() {
|
|||||||
Packer::SelectionConfig sconf;
|
Packer::SelectionConfig sconf;
|
||||||
// sconf.allow_parallel = false;
|
// sconf.allow_parallel = false;
|
||||||
// sconf.force_parallel = false;
|
// sconf.force_parallel = false;
|
||||||
// sconf.try_triplets = true;
|
// sconf.try_triplets = false;
|
||||||
// sconf.try_reverse_order = true;
|
// sconf.try_reverse_order = true;
|
||||||
// sconf.waste_increment = 0.1;
|
// sconf.waste_increment = 0.005;
|
||||||
|
|
||||||
arrange.configure(pconf, sconf);
|
arrange.configure(pconf, sconf);
|
||||||
|
|
||||||
|
@ -25,9 +25,60 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
|
|||||||
|
|
||||||
/// Minkowski addition (not used yet)
|
/// Minkowski addition (not used yet)
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& /*other*/)
|
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
|
||||||
{
|
{
|
||||||
|
using Vertex = TPoint<RawShape>;
|
||||||
|
//using Coord = TCoord<Vertex>;
|
||||||
|
using Edge = _Segment<Vertex>;
|
||||||
|
using sl = ShapeLike;
|
||||||
|
using std::signbit;
|
||||||
|
|
||||||
|
// Copy the orbiter (controur only), we will have to work on it
|
||||||
|
RawShape orbiter = sl::create(sl::getContour(cother));
|
||||||
|
|
||||||
|
// Make the orbiter reverse oriented
|
||||||
|
for(auto &v : sl::getContour(orbiter)) v = -v;
|
||||||
|
|
||||||
|
// An egde with additional data for marking it
|
||||||
|
struct MarkedEdge { Edge e; Radians turn_angle; bool is_turning_point; };
|
||||||
|
|
||||||
|
// Container for marked edges
|
||||||
|
using EdgeList = std::vector<MarkedEdge>;
|
||||||
|
|
||||||
|
EdgeList A, B;
|
||||||
|
|
||||||
|
auto fillEdgeList = [](EdgeList& L, const RawShape& poly) {
|
||||||
|
L.reserve(sl::contourVertexCount(poly));
|
||||||
|
|
||||||
|
auto it = sl::cbegin(poly);
|
||||||
|
auto nextit = std::next(it);
|
||||||
|
|
||||||
|
L.emplace_back({Edge(*it, *nextit), 0, false});
|
||||||
|
it++; nextit++;
|
||||||
|
|
||||||
|
while(nextit != sl::cend(poly)) {
|
||||||
|
Edge e(*it, *nextit);
|
||||||
|
auto& L_prev = L.back();
|
||||||
|
auto phi = L_prev.e.angleToXaxis();
|
||||||
|
auto phi_prev = e.angleToXaxis();
|
||||||
|
auto turn_angle = phi-phi_prev;
|
||||||
|
if(turn_angle > Pi) turn_angle -= 2*Pi;
|
||||||
|
L.emplace_back({
|
||||||
|
e,
|
||||||
|
turn_angle,
|
||||||
|
signbit(turn_angle) != signbit(L_prev.turn_angle)
|
||||||
|
});
|
||||||
|
it++; nextit++;
|
||||||
|
}
|
||||||
|
|
||||||
|
L.front().turn_angle = L.front().e.angleToXaxis() -
|
||||||
|
L.back().e.angleToXaxis();
|
||||||
|
|
||||||
|
if(L.front().turn_angle > Pi) L.front().turn_angle -= 2*Pi;
|
||||||
|
};
|
||||||
|
|
||||||
|
fillEdgeList(A, sh);
|
||||||
|
fillEdgeList(B, orbiter);
|
||||||
|
|
||||||
return sh;
|
return sh;
|
||||||
}
|
}
|
||||||
@ -193,6 +244,9 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
|||||||
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
||||||
// ("No fit polygon problem" - section 2.1.9)
|
// ("No fit polygon problem" - section 2.1.9)
|
||||||
|
|
||||||
|
// TODO: dont do this here. Cache the rmu and lmd in Item and get translate
|
||||||
|
// the nfp after this call
|
||||||
|
|
||||||
auto csh = sh; // Copy sh, we will sort the verices in the copy
|
auto csh = sh; // Copy sh, we will sort the verices in the copy
|
||||||
auto& cmp = _vsort<RawShape>;
|
auto& cmp = _vsort<RawShape>;
|
||||||
std::sort(ShapeLike::begin(csh), ShapeLike::end(csh), cmp);
|
std::sort(ShapeLike::begin(csh), ShapeLike::end(csh), cmp);
|
||||||
|
@ -48,32 +48,89 @@ template<class RawShape> class EdgeCache {
|
|||||||
using Coord = TCoord<Vertex>;
|
using Coord = TCoord<Vertex>;
|
||||||
using Edge = _Segment<Vertex>;
|
using Edge = _Segment<Vertex>;
|
||||||
|
|
||||||
mutable std::vector<double> corners_;
|
struct ContourCache {
|
||||||
|
mutable std::vector<double> corners;
|
||||||
|
std::vector<Edge> emap;
|
||||||
|
std::vector<double> distances;
|
||||||
|
double full_distance = 0;
|
||||||
|
} contour_;
|
||||||
|
|
||||||
std::vector<Edge> emap_;
|
std::vector<ContourCache> holes_;
|
||||||
std::vector<double> distances_;
|
|
||||||
double full_distance_ = 0;
|
|
||||||
|
|
||||||
void createCache(const RawShape& sh) {
|
void createCache(const RawShape& sh) {
|
||||||
auto first = ShapeLike::cbegin(sh);
|
{ // For the contour
|
||||||
auto next = first + 1;
|
auto first = ShapeLike::cbegin(sh);
|
||||||
auto endit = ShapeLike::cend(sh);
|
auto next = std::next(first);
|
||||||
|
auto endit = ShapeLike::cend(sh);
|
||||||
|
|
||||||
distances_.reserve(ShapeLike::contourVertexCount(sh));
|
contour_.distances.reserve(ShapeLike::contourVertexCount(sh));
|
||||||
|
|
||||||
while(next != endit) {
|
while(next != endit) {
|
||||||
emap_.emplace_back(*(first++), *(next++));
|
contour_.emap.emplace_back(*(first++), *(next++));
|
||||||
full_distance_ += emap_.back().length();
|
contour_.full_distance += contour_.emap.back().length();
|
||||||
distances_.push_back(full_distance_);
|
contour_.distances.push_back(contour_.full_distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& h : ShapeLike::holes(sh)) { // For the holes
|
||||||
|
auto first = h.begin();
|
||||||
|
auto next = std::next(first);
|
||||||
|
auto endit = h.end();
|
||||||
|
|
||||||
|
ContourCache hc;
|
||||||
|
hc.distances.reserve(endit - first);
|
||||||
|
|
||||||
|
while(next != endit) {
|
||||||
|
hc.emap.emplace_back(*(first++), *(next++));
|
||||||
|
hc.full_distance += hc.emap.back().length();
|
||||||
|
hc.distances.push_back(hc.full_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
holes_.push_back(hc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchCorners() const {
|
void fetchCorners() const {
|
||||||
if(!corners_.empty()) return;
|
if(!contour_.corners.empty()) return;
|
||||||
|
|
||||||
// TODO Accuracy
|
// TODO Accuracy
|
||||||
corners_ = distances_;
|
contour_.corners = contour_.distances;
|
||||||
for(auto& d : corners_) d /= full_distance_;
|
for(auto& d : contour_.corners) d /= contour_.full_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchHoleCorners(unsigned hidx) const {
|
||||||
|
auto& hc = holes_[hidx];
|
||||||
|
if(!hc.corners.empty()) return;
|
||||||
|
|
||||||
|
// TODO Accuracy
|
||||||
|
hc.corners = hc.distances;
|
||||||
|
for(auto& d : hc.corners) d /= hc.full_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vertex coords(const ContourCache& cache, double distance) const {
|
||||||
|
assert(distance >= .0 && distance <= 1.0);
|
||||||
|
|
||||||
|
// distance is from 0.0 to 1.0, we scale it up to the full length of
|
||||||
|
// the circumference
|
||||||
|
double d = distance*cache.full_distance;
|
||||||
|
|
||||||
|
auto& distances = cache.distances;
|
||||||
|
|
||||||
|
// Magic: we find the right edge in log time
|
||||||
|
auto it = std::lower_bound(distances.begin(), distances.end(), d);
|
||||||
|
auto idx = it - distances.begin(); // get the index of the edge
|
||||||
|
auto edge = cache.emap[idx]; // extrac the edge
|
||||||
|
|
||||||
|
// Get the remaining distance on the target edge
|
||||||
|
auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
|
||||||
|
auto angle = edge.angleToXaxis();
|
||||||
|
Vertex ret = edge.first();
|
||||||
|
|
||||||
|
// Get the point on the edge which lies in ed distance from the start
|
||||||
|
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))),
|
||||||
|
static_cast<Coord>(std::round(ed*std::sin(angle))) };
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -102,37 +159,36 @@ public:
|
|||||||
* @return Returns the coordinates of the point lying on the polygon
|
* @return Returns the coordinates of the point lying on the polygon
|
||||||
* circumference.
|
* circumference.
|
||||||
*/
|
*/
|
||||||
inline Vertex coords(double distance) {
|
inline Vertex coords(double distance) const {
|
||||||
assert(distance >= .0 && distance <= 1.0);
|
return coords(contour_, distance);
|
||||||
|
|
||||||
// distance is from 0.0 to 1.0, we scale it up to the full length of
|
|
||||||
// the circumference
|
|
||||||
double d = distance*full_distance_;
|
|
||||||
|
|
||||||
// Magic: we find the right edge in log time
|
|
||||||
auto it = std::lower_bound(distances_.begin(), distances_.end(), d);
|
|
||||||
auto idx = it - distances_.begin(); // get the index of the edge
|
|
||||||
auto edge = emap_[idx]; // extrac the edge
|
|
||||||
|
|
||||||
// Get the remaining distance on the target edge
|
|
||||||
auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
|
|
||||||
auto angle = edge.angleToXaxis();
|
|
||||||
Vertex ret = edge.first();
|
|
||||||
|
|
||||||
// Get the point on the edge which lies in ed distance from the start
|
|
||||||
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))),
|
|
||||||
static_cast<Coord>(std::round(ed*std::sin(angle))) };
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double circumference() const BP2D_NOEXCEPT { return full_distance_; }
|
inline Vertex coords(unsigned hidx, double distance) const {
|
||||||
|
assert(hidx < holes_.size());
|
||||||
|
return coords(holes_[hidx], distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double circumference() const BP2D_NOEXCEPT {
|
||||||
|
return contour_.full_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double circumference(unsigned hidx) const BP2D_NOEXCEPT {
|
||||||
|
return holes_[hidx].full_distance;
|
||||||
|
}
|
||||||
|
|
||||||
inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
|
inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
|
||||||
fetchCorners();
|
fetchCorners();
|
||||||
return corners_;
|
return contour_.corners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const std::vector<double>&
|
||||||
|
corners(unsigned holeidx) const BP2D_NOEXCEPT {
|
||||||
|
fetchHoleCorners(holeidx);
|
||||||
|
return holes_[holeidx].corners;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<NfpLevel lvl>
|
template<NfpLevel lvl>
|
||||||
@ -294,12 +350,20 @@ public:
|
|||||||
|
|
||||||
for(auto& nfp : nfps ) ecache.emplace_back(nfp);
|
for(auto& nfp : nfps ) ecache.emplace_back(nfp);
|
||||||
|
|
||||||
auto getNfpPoint = [&ecache](double relpos) {
|
struct Optimum {
|
||||||
auto relpfloor = std::floor(relpos);
|
double relpos;
|
||||||
auto nfp_idx = static_cast<unsigned>(relpfloor);
|
unsigned nfpidx;
|
||||||
if(nfp_idx >= ecache.size()) nfp_idx--;
|
int hidx;
|
||||||
auto p = relpos - relpfloor;
|
Optimum(double pos, unsigned nidx):
|
||||||
return ecache[nfp_idx].coords(p);
|
relpos(pos), nfpidx(nidx), hidx(-1) {}
|
||||||
|
Optimum(double pos, unsigned nidx, int holeidx):
|
||||||
|
relpos(pos), nfpidx(nidx), hidx(holeidx) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getNfpPoint = [&ecache](const Optimum& opt)
|
||||||
|
{
|
||||||
|
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
|
||||||
|
ecache[opt.nfpidx].coords(opt.nfpidx, opt.relpos);
|
||||||
};
|
};
|
||||||
|
|
||||||
Nfp::Shapes<RawShape> pile;
|
Nfp::Shapes<RawShape> pile;
|
||||||
@ -310,6 +374,8 @@ public:
|
|||||||
pile_area += mitem.area();
|
pile_area += mitem.area();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is the kernel part of the object function that is
|
||||||
|
// customizable by the library client
|
||||||
auto _objfunc = config_.object_function?
|
auto _objfunc = config_.object_function?
|
||||||
config_.object_function :
|
config_.object_function :
|
||||||
[this](const Nfp::Shapes<RawShape>& pile, double occupied_area,
|
[this](const Nfp::Shapes<RawShape>& pile, double occupied_area,
|
||||||
@ -334,9 +400,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Our object function for placement
|
// Our object function for placement
|
||||||
auto objfunc = [&] (double relpos)
|
auto rawobjfunc = [&] (Vertex v)
|
||||||
{
|
{
|
||||||
Vertex v = getNfpPoint(relpos);
|
|
||||||
auto d = v - iv;
|
auto d = v - iv;
|
||||||
d += startpos;
|
d += startpos;
|
||||||
item.translation(d);
|
item.translation(d);
|
||||||
@ -359,46 +424,74 @@ public:
|
|||||||
stopcr.type = opt::StopLimitType::RELATIVE;
|
stopcr.type = opt::StopLimitType::RELATIVE;
|
||||||
opt::TOptimizer<opt::Method::L_SIMPLEX> solver(stopcr);
|
opt::TOptimizer<opt::Method::L_SIMPLEX> solver(stopcr);
|
||||||
|
|
||||||
double optimum = 0;
|
Optimum optimum(0, 0);
|
||||||
double best_score = penality_;
|
double best_score = penality_;
|
||||||
|
|
||||||
// double max_bound = 1.0*nfps.size();
|
|
||||||
// Genetic should look like this:
|
|
||||||
/*auto result = solver.optimize_min(objfunc,
|
|
||||||
opt::initvals<double>(0.0),
|
|
||||||
opt::bound(0.0, max_bound)
|
|
||||||
);
|
|
||||||
|
|
||||||
if(result.score < penality_) {
|
|
||||||
best_score = result.score;
|
|
||||||
optimum = std::get<0>(result.optimum);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Local optimization with the four polygon corners as
|
// Local optimization with the four polygon corners as
|
||||||
// starting points
|
// starting points
|
||||||
for(unsigned ch = 0; ch < ecache.size(); ch++) {
|
for(unsigned ch = 0; ch < ecache.size(); ch++) {
|
||||||
auto& cache = ecache[ch];
|
auto& cache = ecache[ch];
|
||||||
|
|
||||||
|
auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch]
|
||||||
|
(double relpos)
|
||||||
|
{
|
||||||
|
return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
|
||||||
|
};
|
||||||
|
|
||||||
std::for_each(cache.corners().begin(),
|
std::for_each(cache.corners().begin(),
|
||||||
cache.corners().end(),
|
cache.corners().end(),
|
||||||
[ch, &solver, &objfunc,
|
[ch, &contour_ofn, &solver, &best_score,
|
||||||
&best_score, &optimum]
|
&optimum] (double pos)
|
||||||
(double pos)
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto result = solver.optimize_min(objfunc,
|
auto result = solver.optimize_min(contour_ofn,
|
||||||
opt::initvals<double>(ch+pos),
|
opt::initvals<double>(pos),
|
||||||
opt::bound<double>(ch, 1.0 + ch)
|
opt::bound<double>(0, 1.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
if(result.score < best_score) {
|
if(result.score < best_score) {
|
||||||
best_score = result.score;
|
best_score = result.score;
|
||||||
optimum = std::get<0>(result.optimum);
|
optimum.relpos = std::get<0>(result.optimum);
|
||||||
|
optimum.nfpidx = ch;
|
||||||
|
optimum.hidx = -1;
|
||||||
}
|
}
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
derr() << "ERROR: " << e.what() << "\n";
|
derr() << "ERROR: " << e.what() << "\n";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
|
||||||
|
auto hole_ofn =
|
||||||
|
[&rawobjfunc, &getNfpPoint, ch, hidx]
|
||||||
|
(double pos)
|
||||||
|
{
|
||||||
|
Optimum opt(pos, ch, hidx);
|
||||||
|
return rawobjfunc(getNfpPoint(opt));
|
||||||
|
};
|
||||||
|
|
||||||
|
std::for_each(cache.corners(hidx).begin(),
|
||||||
|
cache.corners(hidx).end(),
|
||||||
|
[&hole_ofn, &solver, &best_score,
|
||||||
|
&optimum, ch, hidx]
|
||||||
|
(double pos)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
auto result = solver.optimize_min(hole_ofn,
|
||||||
|
opt::initvals<double>(pos),
|
||||||
|
opt::bound<double>(0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(result.score < best_score) {
|
||||||
|
best_score = result.score;
|
||||||
|
Optimum o(std::get<0>(result.optimum),
|
||||||
|
ch, hidx);
|
||||||
|
optimum = o;
|
||||||
|
}
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
derr() << "ERROR: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( best_score < global_score ) {
|
if( best_score < global_score ) {
|
||||||
|
@ -56,7 +56,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Safety test: try to pack each item into an empty bin. If it fails
|
// Safety test: try to pack each item into an empty bin. If it fails
|
||||||
// then it should be removed from the not_packed list
|
// then it should be removed from the list
|
||||||
{ auto it = store_.begin();
|
{ auto it = store_.begin();
|
||||||
while (it != store_.end()) {
|
while (it != store_.end()) {
|
||||||
Placer p(bin);
|
Placer p(bin);
|
||||||
@ -72,7 +72,7 @@ public:
|
|||||||
while(!was_packed) {
|
while(!was_packed) {
|
||||||
|
|
||||||
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
||||||
if(was_packed = placers[j].pack(item))
|
if((was_packed = placers[j].pack(item)))
|
||||||
makeProgress(placers[j], j);
|
makeProgress(placers[j], j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,12 +530,12 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
|
|||||||
// arranger.useMinimumBoundigBoxRotation();
|
// arranger.useMinimumBoundigBoxRotation();
|
||||||
pcfg.rotations = { 0.0 };
|
pcfg.rotations = { 0.0 };
|
||||||
|
|
||||||
// Magic: we will specify what is the goal of arrangement...
|
// Magic: we will specify what is the goal of arrangement... In this case
|
||||||
// In this case we override the default object to make the larger items go
|
// we override the default object function to make the larger items go into
|
||||||
// into the center of the pile and smaller items orbit it so the resulting
|
// the center of the pile and smaller items orbit it so the resulting pile
|
||||||
// pile has a circle-like shape. This is good for the print bed's heat
|
// has a circle-like shape. This is good for the print bed's heat profile.
|
||||||
// profile. We alse sacrafice a bit of pack efficiency for this to work. As
|
// We alse sacrafice a bit of pack efficiency for this to work. As a side
|
||||||
// a side effect, the arrange procedure is a lot faster (we do not need to
|
// effect, the arrange procedure is a lot faster (we do not need to
|
||||||
// calculate the convex hulls)
|
// calculate the convex hulls)
|
||||||
pcfg.object_function = [bin, hasbin](
|
pcfg.object_function = [bin, hasbin](
|
||||||
NfpPlacer::Pile pile, // The currently arranged pile
|
NfpPlacer::Pile pile, // The currently arranged pile
|
||||||
|
Loading…
Reference in New Issue
Block a user