Better support for circular bed.

This commit is contained in:
tamasmeszaros 2018-08-06 20:13:04 +02:00
parent 3c32a7c3db
commit e1edb05bbb
5 changed files with 172 additions and 530 deletions

View File

@ -50,492 +50,12 @@ void arrangeRectangles() {
using namespace libnest2d; using namespace libnest2d;
const int SCALE = 1000000; const int SCALE = 1000000;
// const int SCALE = 1;
std::vector<Rectangle> rects = { std::vector<Rectangle> rects = {
{80*SCALE, 80*SCALE}, {60*SCALE, 200*SCALE},
{60*SCALE, 90*SCALE}, {60*SCALE, 200*SCALE}
{70*SCALE, 30*SCALE},
{80*SCALE, 60*SCALE},
{60*SCALE, 60*SCALE},
{60*SCALE, 40*SCALE},
{40*SCALE, 40*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{20*SCALE, 20*SCALE}
};
// std::vector<Rectangle> rects = {
// {20*SCALE, 10*SCALE},
// {20*SCALE, 10*SCALE},
// {20*SCALE, 20*SCALE},
// };
// std::vector<Item> input {
// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
// };
std::vector<Item> crasher =
{
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-18000000, -1000000},
{-15000000, 22000000},
{-11000000, 26000000},
{11000000, 26000000},
{15000000, 22000000},
{18000000, -1000000},
{18000000, -26000000},
{-18000000, -26000000},
{-18000000, -1000000},
},
}; };
std::vector<Item> proba = {
{
Rectangle(100, 2)
},
{
Rectangle(100, 2)
},
{
Rectangle(100, 2)
},
{
Rectangle(10, 10)
},
};
proba[0].rotate(Pi/3);
proba[1].rotate(Pi-Pi/3);
// std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
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());
@ -544,7 +64,7 @@ void arrangeRectangles() {
// 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);
// PolygonImpl bin = { // PolygonImpl bin = {
// { // {
// {25*SCALE, 0}, // {25*SCALE, 0},
@ -560,9 +80,11 @@ void arrangeRectangles() {
// {} // {}
// }; // };
_Circle<PointImpl> bin({0, 0}, 125*SCALE);
auto min_obj_distance = static_cast<Coord>(0*SCALE); auto min_obj_distance = static_cast<Coord>(0*SCALE);
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>; using Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
using Packer = Arranger<Placer, FirstFitSelection>; using Packer = Arranger<Placer, FirstFitSelection>;
Packer arrange(bin, min_obj_distance); Packer arrange(bin, min_obj_distance);
@ -571,9 +93,9 @@ void arrangeRectangles() {
pconf.alignment = Placer::Config::Alignment::CENTER; pconf.alignment = Placer::Config::Alignment::CENTER;
pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER;
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
pconf.accuracy = 0.5f; pconf.accuracy = 1.0f;
// auto bincenter = ShapeLike::boundingBox(bin).center(); // auto bincenter = ShapeLike::boundingBox<PolygonImpl>(bin).center();
// pconf.object_function = [&bin, bincenter]( // pconf.object_function = [&bin, bincenter](
// Placer::Pile pile, const Item& item, // Placer::Pile pile, const Item& item,
// double /*area*/, double norm, double penality) { // double /*area*/, double norm, double penality) {
@ -660,10 +182,7 @@ void arrangeRectangles() {
// score = pl::distance(ibb.center(), bigbb.center()) / norm; // score = pl::distance(ibb.center(), bigbb.center()) / norm;
// } // }
// // If it does not fit into the print bed we will beat it // if(!Placer::wouldFit(fullbb, bin)) score += norm;
// // with a large penality. If we would not do this, there would be only
// // one big pile that doesn't care whether it fits onto the print bed.
// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
// return score; // return score;
// }; // };

View File

@ -109,7 +109,7 @@ public:
inline void radius(double r) { radius_ = r; } inline void radius(double r) { radius_ = r; }
inline double area() const BP2D_NOEXCEPT { inline double area() const BP2D_NOEXCEPT {
return 2.0*Pi*radius_; return 2.0*Pi*radius_*radius_;
} }
}; };

View File

@ -1,16 +1,19 @@
#ifndef NOFITPOLY_HPP #ifndef NOFITPOLY_HPP
#define NOFITPOLY_HPP #define NOFITPOLY_HPP
#include <cassert>
#include <random>
#ifndef NDEBUG #ifndef NDEBUG
#include <iostream> #include <iostream>
#endif #endif
#include "placer_boilerplate.hpp" #include "placer_boilerplate.hpp"
#include "../geometry_traits_nfp.hpp" #include "../geometry_traits_nfp.hpp"
#include "libnest2d/optimizer.hpp" #include "libnest2d/optimizer.hpp"
#include <cassert>
#include "tools/svgtools.hpp" #include "tools/svgtools.hpp"
namespace libnest2d { namespace strategies { namespace libnest2d { namespace strategies {
template<class RawShape> template<class RawShape>
@ -161,12 +164,11 @@ template<class RawShape> class EdgeCache {
} }
size_t stride(const size_t N) const { size_t stride(const size_t N) const {
using std::ceil;
using std::round; using std::round;
using std::pow; using std::pow;
return static_cast<Coord>( return static_cast<Coord>(
std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0))) round(N/pow(N, pow(accuracy_, 1.0/3.0)))
); );
} }
@ -177,6 +179,7 @@ template<class RawShape> class EdgeCache {
const auto S = stride(N); const auto S = stride(N);
contour_.corners.reserve(N / S + 1); contour_.corners.reserve(N / S + 1);
contour_.corners.emplace_back(0.0);
auto N_1 = N-1; auto N_1 = N-1;
contour_.corners.emplace_back(0.0); contour_.corners.emplace_back(0.0);
for(size_t i = 0; i < N_1; i += S) { for(size_t i = 0; i < N_1; i += S) {
@ -190,8 +193,8 @@ template<class RawShape> class EdgeCache {
if(!hc.corners.empty()) return; if(!hc.corners.empty()) return;
const auto N = hc.distances.size(); const auto N = hc.distances.size();
const auto S = stride(N);
auto N_1 = N-1; auto N_1 = N-1;
const auto S = stride(N);
hc.corners.reserve(N / S + 1); hc.corners.reserve(N / S + 1);
hc.corners.emplace_back(0.0); hc.corners.emplace_back(0.0);
for(size_t i = 0; i < N_1; i += S) { for(size_t i = 0; i < N_1; i += S) {
@ -339,7 +342,7 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
Nfp::Shapes<RawShape> nfps; Nfp::Shapes<RawShape> nfps;
//int pi = 0; // int pi = 0;
for(Item& sh : polygons) { for(Item& sh : polygons) {
auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>( auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
sh.transformedShape(), trsh.transformedShape()); sh.transformedShape(), trsh.transformedShape());
@ -441,6 +444,63 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
// return nfps; // return nfps;
} }
template<class RawShape>
_Circle<TPoint<RawShape>> minimizeCircle(const RawShape& sh) {
using sl = ShapeLike; using pl = PointLike;
using Point = TPoint<RawShape>;
using Coord = TCoord<Point>;
auto bb = sl::boundingBox(sh);
auto capprx = bb.center();
auto rapprx = pl::distance(bb.minCorner(), bb.maxCorner());
auto& ctr = sl::getContour(sh);
opt::StopCriteria stopcr;
stopcr.max_iterations = 100;
stopcr.relative_score_difference = 1e-3;
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
std::vector<double> dists(ctr.size(), 0);
auto result = solver.optimize_min(
[capprx, rapprx, &ctr, &dists](double xf, double yf) {
auto xt = Coord( std::round(getX(capprx) + rapprx*xf) );
auto yt = Coord( std::round(getY(capprx) + rapprx*yf) );
Point centr(xt, yt);
unsigned i = 0;
for(auto v : ctr) {
dists[i++] = pl::distance(v, centr);
}
auto mit = std::max_element(dists.begin(), dists.end());
assert(mit != dists.end());
return *mit;
},
opt::initvals(0.0, 0.0),
opt::bound(-1.0, 1.0), opt::bound(-1.0, 1.0)
);
double oxf = std::get<0>(result.optimum);
double oyf = std::get<1>(result.optimum);
auto xt = Coord( std::round(getX(capprx) + rapprx*oxf) );
auto yt = Coord( std::round(getY(capprx) + rapprx*oyf) );
Point cc(xt, yt);
auto r = result.score;
return {cc, r};
}
template<class RawShape>
_Circle<TPoint<RawShape>> boundingCircle(const RawShape& sh) {
return minimizeCircle(sh);
}
template<class RawShape, class TBin = _Box<TPoint<RawShape>>> template<class RawShape, class TBin = _Box<TPoint<RawShape>>>
class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>, class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
RawShape, TBin, NfpPConfig<RawShape>> { RawShape, TBin, NfpPConfig<RawShape>> {
@ -512,11 +572,7 @@ public:
bool static inline wouldFit(const RawShape& chull, bool static inline wouldFit(const RawShape& chull,
const _Circle<Vertex>& bin) const _Circle<Vertex>& bin)
{ {
auto bb = sl::boundingBox(chull); return boundingCircle(chull).radius() < bin.radius();
auto d = bin.center() - bb.center();
auto chullcpy = chull;
sl::translate(chullcpy, d);
return sl::isInside<RawShape>(chullcpy, bin);
} }
PackResult trypack(Item& item) { PackResult trypack(Item& item) {
@ -574,8 +630,9 @@ public:
auto getNfpPoint = [&ecache](const Optimum& opt) auto getNfpPoint = [&ecache](const Optimum& opt)
{ {
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) : auto ret = opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
return ret;
}; };
Nfp::Shapes<RawShape> pile; Nfp::Shapes<RawShape> pile;
@ -595,7 +652,7 @@ public:
[this, &merged_pile]( [this, &merged_pile](
Nfp::Shapes<RawShape>& /*pile*/, Nfp::Shapes<RawShape>& /*pile*/,
const Item& item, const Item& item,
double occupied_area, double occupied_area,
double norm, double norm,
double /*penality*/) double /*penality*/)
{ {
@ -751,14 +808,37 @@ public:
} }
inline void clearItems() { inline void clearItems() {
finalAlign(bin_);
Base::clearItems();
}
private:
inline void finalAlign(const RawShape& pbin) {
auto bbin = sl::boundingBox(pbin);
finalAlign(bbin);
}
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
if(items_.empty()) return;
Nfp::Shapes<RawShape> m; Nfp::Shapes<RawShape> m;
m.reserve(items_.size()); m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
auto c = boundingCircle(sl::convexHull(m));
auto d = cbin.center() - c.center();
for(Item& item : items_) item.translate(d);
}
inline void finalAlign(Box bbin) {
Nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape()); for(Item& item : items_) m.emplace_back(item.transformedShape());
auto&& bb = sl::boundingBox<RawShape>(m); auto&& bb = sl::boundingBox<RawShape>(m);
Vertex ci, cb; Vertex ci, cb;
auto bbin = sl::boundingBox<RawShape>(bin_);
switch(config_.alignment) { switch(config_.alignment) {
case Config::Alignment::CENTER: { case Config::Alignment::CENTER: {
@ -790,12 +870,8 @@ public:
auto d = cb - ci; auto d = cb - ci;
for(Item& item : items_) item.translate(d); for(Item& item : items_) item.translate(d);
Base::clearItems();
} }
private:
void setInitialPosition(Item& item) { void setInitialPosition(Item& item) {
Box&& bb = item.boundingBox(); Box&& bb = item.boundingBox();
Vertex ci, cb; Vertex ci, cb;

View File

@ -99,6 +99,43 @@ TEST(BasicFunctionality, creationAndDestruction)
} }
TEST(GeometryAlgorithms, boundingCircle) {
using namespace libnest2d;
using strategies::boundingCircle;
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
_Circle<PointImpl> c = boundingCircle<PolygonImpl>(p);
ASSERT_EQ(c.center().X, 0);
ASSERT_EQ(c.center().Y, 0);
ASSERT_DOUBLE_EQ(c.radius(), 10);
ShapeLike::translate(p, PointImpl{10, 10});
c = boundingCircle<PolygonImpl>(p);
ASSERT_EQ(c.center().X, 10);
ASSERT_EQ(c.center().Y, 10);
ASSERT_DOUBLE_EQ(c.radius(), 10);
auto parts = prusaParts();
int i = 0;
for(auto& part : parts) {
c = boundingCircle(part.transformedShape());
if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
else for(auto v : ShapeLike::getContour(part.transformedShape()) ) {
auto d = PointLike::distance(v, c.center());
if(d > c.radius() ) {
auto e = std::abs( 1.0 - d/c.radius());
ASSERT_LE(e, 1e-3);
}
}
i++;
}
}
TEST(GeometryAlgorithms, Distance) { TEST(GeometryAlgorithms, Distance) {
using namespace libnest2d; using namespace libnest2d;

View File

@ -102,9 +102,9 @@ using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
std::tuple<double /*score*/, Box /*farthest point from bin center*/> std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter, objfunc(const PointImpl& bincenter,
double /*bin_area*/, double bin_area,
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
double /*pile_area*/, double pile_area,
const Item &item, const Item &item,
double norm, // A norming factor for physical dimensions double norm, // A norming factor for physical dimensions
std::vector<double>& areacache, // pile item areas will be cached std::vector<double>& areacache, // pile item areas will be cached
@ -115,12 +115,16 @@ objfunc(const PointImpl& bincenter,
using pl = PointLike; using pl = PointLike;
using sl = ShapeLike; using sl = ShapeLike;
static const double BIG_ITEM_TRESHOLD = 0.2; static const double BIG_ITEM_TRESHOLD = 0.04;
static const double ROUNDNESS_RATIO = 0.5; static const double ROUNDNESS_RATIO = 0.5;
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
// We will treat big items (compared to the print bed) differently // We will treat big items (compared to the print bed) differently
auto normarea = [norm](double area) { return std::sqrt(area)/norm; };
auto isBig = [&areacache, bin_area](double a) {
bool t = areacache.empty() ? true : a > 0.5*areacache.front();
return a/bin_area > BIG_ITEM_TRESHOLD || t;
};
// If a new bin has been created: // If a new bin has been created:
if(pile.size() < areacache.size()) { if(pile.size() < areacache.size()) {
@ -133,7 +137,7 @@ objfunc(const PointImpl& bincenter,
for(auto& p : pile) { for(auto& p : pile) {
if(idx == areacache.size()) { if(idx == areacache.size()) {
areacache.emplace_back(sl::area(p)); areacache.emplace_back(sl::area(p));
if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD) if(isBig(areacache[idx]))
spatindex.insert({sl::boundingBox(p), idx}); spatindex.insert({sl::boundingBox(p), idx});
} }
@ -157,14 +161,10 @@ objfunc(const PointImpl& bincenter,
boost::geometry::convert(boostbb, bigbb); boost::geometry::convert(boostbb, bigbb);
} }
// The size indicator of the candidate item. This is not the area,
// but almost...
double item_normarea = normarea(item.area());
// Will hold the resulting score // Will hold the resulting score
double score = 0; double score = 0;
if(item_normarea > BIG_ITEM_TRESHOLD) { if(isBig(item.area())) {
// This branch is for the bigger items.. // This branch is for the bigger items..
// Here we will use the closest point of the item bounding box to // Here we will use the closest point of the item bounding box to
// the already arranged pile. So not the bb center nor the a choosen // the already arranged pile. So not the bb center nor the a choosen
@ -223,10 +223,9 @@ objfunc(const PointImpl& bincenter,
// The final mix of the score is the balance between the distance // The final mix of the score is the balance between the distance
// from the full pile center, the pack density and the // from the full pile center, the pack density and the
// alignment with the neigbours // alignment with the neigbours
auto C = 0.33; score = 0.4 * dist + 0.4 * density + 0.2 * alignment_score;
score = C * dist + C * density + C * alignment_score;
} else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) { } else if( !isBig(item.area()) && spatindex.empty()) {
// If there are no big items, only small, we should consider the // If there are no big items, only small, we should consider the
// density here as well to not get silly results // density here as well to not get silly results
auto bindist = pl::distance(ibb.center(), bincenter) / norm; auto bindist = pl::distance(ibb.center(), bincenter) / norm;
@ -349,17 +348,26 @@ public:
auto result = objfunc(bin.center(), bin_area_, pile, auto result = objfunc(bin.center(), bin_area_, pile,
pile_area, item, norm, areacache_, rtree_); pile_area, item, norm, areacache_, rtree_);
double score = std::get<0>(result); double score = std::get<0>(result);
// Circle fitting detection is very rough at the moment but
// we still need something that tells how badly the arrangement
// misses the print bed.
auto& fullbb = std::get<1>(result); auto& fullbb = std::get<1>(result);
auto bbr = 0.5*PointLike::distance(fullbb.minCorner(),
fullbb.maxCorner());
auto diff = bbr - bin.radius();
if(diff > 0) score += std::pow(diff, 2) / norm; auto d = PointLike::distance(fullbb.minCorner(),
fullbb.maxCorner());
auto diff = d - 2*bin.radius();
if(diff > 0) {
if( item.area() > 0.01*bin_area_ && item.vertexCount() < 20) {
pile.emplace_back(item.transformedShape());
auto chull = ShapeLike::convexHull(pile);
pile.pop_back();
auto C = strategies::boundingCircle(chull);
auto rdiff = C.radius() - bin.radius();
if(rdiff > 0) {
score += std::pow(rdiff, 3) / norm;
}
}
}
return score; return score;
}; };
@ -695,6 +703,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
} }
}; };
if(result.empty()) return false;
if(first_bin_only) { if(first_bin_only) {
applyResult(result.front(), 0, shapemap); applyResult(result.front(), 0, shapemap);
} else { } else {