Extend libnest tests, remove some warnings, faster catch2 compilation.

Also, improve libnest2d::nest interface.
This commit is contained in:
tamasmeszaros 2019-10-07 17:16:40 +02:00
parent 555fcc151d
commit 72ac8d68f0
7 changed files with 175 additions and 129 deletions

View File

@ -49,91 +49,84 @@ using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
extern template class Nester<NfpPlacer, FirstFitSelection>;
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
extern template PackGroup Nester<NfpPlacer, FirstFitSelection>::execute(
extern template std::size_t Nester<NfpPlacer, FirstFitSelection>::execute(
std::vector<Item>::iterator, std::vector<Item>::iterator);
extern template PackGroup Nester<BottomLeftPlacer, FirstFitSelection>::execute(
extern template std::size_t Nester<BottomLeftPlacer, FirstFitSelection>::execute(
std::vector<Item>::iterator, std::vector<Item>::iterator);
#endif
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Iterator = std::vector<Item>::iterator>
void nest(Iterator from, Iterator to,
const typename Placer::BinType& bin,
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
{
_Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
nester.execute(from, to);
}
template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
struct NestConfig {
typename Placer::Config placer_config;
typename Selector::Config selector_config;
using Placement = typename Placer::Config;
using Selection = typename Selector::Config;
NestConfig() = default;
NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
NestConfig(const typename Placer::Config & pcfg,
const typename Selector::Config &scfg)
: placer_config{pcfg}, selector_config{scfg} {}
};
struct NestControl {
ProgressFunction progressfn;
StopCondition stopcond = []{ return false; };
NestControl() = default;
NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
NestControl(ProgressFunction pr, StopCondition sc)
: progressfn{std::move(pr)}, stopcond{std::move(sc)}
{}
};
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Iterator = std::vector<Item>::iterator>
void nest(Iterator from, Iterator to,
std::size_t nest(Iterator from, Iterator to,
const typename Placer::BinType & bin,
ProgressFunction prg,
StopCondition scond = []() { return false; },
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
const NestConfig<Placer, Selector> &cfg = {},
NestControl ctl = {})
{
_Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
if(prg) nester.progressIndicator(prg);
if(scond) nester.stopCondition(scond);
nester.execute(from, to);
_Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
return nester.execute(from, to);
}
#ifdef LIBNEST2D_STATIC
extern template class Nester<NfpPlacer, FirstFitSelection>;
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
extern template void nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
extern template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
extern template void nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
Coord dist,
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
NestControl ctl);
extern template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
ProgressFunction prg,
StopCondition scond,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
Coord dist,
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
NestControl ctl);
#endif
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Container = std::vector<Item>>
void nest(Container&& cont,
std::size_t nest(Container&& cont,
const typename Placer::BinType & bin,
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
const NestConfig<Placer, Selector> &cfg = {},
NestControl ctl = {})
{
nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, pconf, sconf);
}
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Container = std::vector<Item>>
void nest(Container&& cont,
const typename Placer::BinType& bin,
ProgressFunction prg,
StopCondition scond = []() { return false; },
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
{
nest<Placer, Selector>(cont.begin(), cont.end(), bin, prg, scond, dist,
pconf, sconf);
return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
}
}

View File

@ -129,7 +129,11 @@ public:
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
inline bool isFixed() const noexcept { return fixed_; }
inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
inline void markAsFixedInBin(int binid)
{
fixed_ = binid >= 0;
binid_ = binid;
}
inline void binId(int idx) { binid_ = idx; }
inline int binId() const noexcept { return binid_; }
@ -748,6 +752,7 @@ template<class PlacementStrategy, class SelectionStrategy >
class _Nester {
using TSel = SelectionStrategyLike<SelectionStrategy>;
TSel selector_;
public:
using Item = typename PlacementStrategy::Item;
using ShapeType = typename Item::ShapeType;
@ -824,7 +829,7 @@ public:
* the selection algorithm.
*/
template<class It>
inline ItemIteratorOnly<It, void> execute(It from, It to)
inline ItemIteratorOnly<It, size_t> 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, infl](Item& item) {
@ -837,6 +842,8 @@ public:
if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
item.inflate(-infl);
});
return selector_.getResult().size();
}
/// Set a progress indicator function object for the selector.

View File

@ -5,19 +5,17 @@ namespace libnest2d {
template class Nester<NfpPlacer, FirstFitSelection>;
template class Nester<BottomLeftPlacer, FirstFitSelection>;
template PackGroup nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
Coord dist,
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
NestControl ctl);
template PackGroup nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
ProgressFunction prg,
StopCondition scond,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
Coord dist,
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
NestControl ctl);
}

View File

@ -375,7 +375,7 @@ public:
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
Item& itm = fixeditems[idx];
itm.markAsFixed();
itm.markAsFixedInBin(0);
}
m_pck.configure(m_pconf);

View File

@ -1571,7 +1571,8 @@ struct Plater::priv
size_t count = 0; // To know how much space to reserve
for (auto obj : model.objects) count += obj->instances.size();
m_selected.clear(), m_unselected.clear();
m_selected.clear();
m_unselected.clear();
m_selected.reserve(count + 1 /* for optional wti */);
m_unselected.reserve(count + 1 /* for optional wti */);
}
@ -1589,7 +1590,8 @@ struct Plater::priv
ap.bed_idx = ap.translation.x() / bed_stride();
ap.setter = [obj, this](const ArrangePolygon &p) {
if (p.is_arranged()) {
auto t = p.translation; t.x() += p.bed_idx * bed_stride();
auto t = p.translation;
t.x() += p.bed_idx * bed_stride();
obj->apply_arrange_result(t, p.rotation);
}
};
@ -1620,7 +1622,8 @@ struct Plater::priv
obj_sel(model.objects.size(), nullptr);
for (auto &s : plater().get_selection().get_content())
if (s.first < int(obj_sel.size())) obj_sel[s.first] = &s.second;
if (s.first < int(obj_sel.size()))
obj_sel[size_t(s.first)] = &s.second;
// Go through the objects and check if inside the selection
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
@ -1630,7 +1633,8 @@ struct Plater::priv
std::vector<bool> inst_sel(mo->instances.size(), false);
if (instlist)
for (auto inst_id : *instlist) inst_sel[inst_id] = true;
for (auto inst_id : *instlist)
inst_sel[size_t(inst_id)] = true;
for (size_t i = 0; i < inst_sel.size(); ++i) {
ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]);
@ -2759,9 +2763,8 @@ void Plater::priv::ArrangeJob::process() {
try {
arrangement::arrange(m_selected, m_unselected, min_d, bedshape,
[this, count](unsigned st) {
if (st >
0) // will not finalize after last one
update_status(count - st, arrangestr);
if (st > 0) // will not finalize after last one
update_status(int(count - st), arrangestr);
},
[this]() { return was_canceled(); });
} catch (std::exception & /*e*/) {

View File

@ -11,7 +11,7 @@ add_library(Catch2::Catch2 ALIAS Catch2)
include(Catch)
add_library(test_catch2_common INTERFACE)
target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2)
add_library(test_common INTERFACE)

View File

@ -1,5 +1,7 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <catch2/catch_reporter_teamcity.hpp>
#include <fstream>
#include <libnest2d.h>
@ -225,11 +227,11 @@ TEST_CASE("Area", "[Geometry]") {
RectangleItem rect(10, 10);
REQUIRE(rect.area() == 100);
REQUIRE(rect.area() == Approx(100));
RectangleItem rect2 = {100, 100};
REQUIRE(rect2.area() == 10000);
REQUIRE(rect2.area() == Approx(10000));
Item item = {
{61, 97},
@ -288,11 +290,9 @@ TEST_CASE("IsPointInsidePolygon", "[Geometry]") {
// REQUIRE_FALSE(ShapeLike::intersects(s1, s2));
//}
// Simple TEST_CASE, does not use gmock
TEST_CASE("LeftAndDownPolygon", "[Geometry]")
{
using namespace libnest2d;
using namespace libnest2d;
Box bin(100, 100);
BottomLeftPlacer placer(bin);
@ -339,7 +339,6 @@ TEST_CASE("LeftAndDownPolygon", "[Geometry]")
}
}
// Simple TEST_CASE, does not use gmock
TEST_CASE("ArrangeRectanglesTight", "[Nesting]")
{
using namespace libnest2d;
@ -450,7 +449,7 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
return i1.binId() < i2.binId();
});
size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
auto groups = size_t(max_group == rects.end() ? 0 : max_group->binId() + 1);
REQUIRE(groups == 1u);
REQUIRE(
@ -477,8 +476,6 @@ using namespace libnest2d;
template<long long SCALE = 1, class Bin>
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin, int idx = 0) {
std::string loc = "out";
static std::string svg_header =
@ -494,20 +491,20 @@ void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin
if(out.is_open()) {
out << svg_header;
Item rbin( RectangleItem(bin.width(), bin.height()) );
for(unsigned i = 0; i < rbin.vertexCount(); i++) {
auto v = rbin.vertex(i);
for(unsigned j = 0; j < rbin.vertexCount(); j++) {
auto v = rbin.vertex(j);
setY(v, -getY(v)/SCALE + 500 );
setX(v, getX(v)/SCALE);
rbin.setVertex(i, v);
rbin.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(Item& sh : r) {
Item tsh(sh.transformedShape());
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
auto v = tsh.vertex(i);
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
auto v = tsh.vertex(j);
setY(v, -getY(v)/SCALE + 500);
setX(v, getX(v)/SCALE);
tsh.setVertex(i, v);
tsh.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
@ -570,7 +567,7 @@ TEST_CASE("convexHull", "[Geometry]") {
REQUIRE(chull.size() == poly.size());
}
TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
// Get the input items and define the bin.
std::vector<Item> input = prusaParts();
@ -579,21 +576,15 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
// Do the nesting. Check in each step if the remaining items are less than
// in the previous step. (Some algorithms can place more items in one step)
size_t pcount = input.size();
libnest2d::nest(input, bin, [&pcount](unsigned cnt) {
size_t bins = libnest2d::nest(input, bin, 0, {},
ProgressFunction{[&pcount](unsigned cnt) {
REQUIRE(cnt < pcount);
pcount = cnt;
});
// Get the number of logical bins: search for the max binId...
auto max_binid_it = std::max_element(input.begin(), input.end(),
[](const Item &i1, const Item &i2) {
return i1.binId() < i2.binId();
});
auto bins = size_t(max_binid_it == input.end() ? 0 :
max_binid_it->binId() + 1);
}});
// For prusa parts, 2 bins should be enough...
REQUIRE(bins > 0u);
REQUIRE(bins <= 2u);
// All parts should be processed by the algorithm
@ -617,29 +608,83 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
}
}
TEST_CASE("NestEmptyItemShouldBeUntouched", "[Nesting]") {
TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") {
auto bin = Box(250000000, 210000000); // dummy bin
std::vector<Item> items;
items.emplace_back(Item{}); // Emplace empty item
items.emplace_back(Item{0, 200, 0}); // Emplace zero area item
libnest2d::nest(items, bin);
size_t bins = libnest2d::nest(items, bin);
REQUIRE(bins == 0u);
for (auto &itm : items) REQUIRE(itm.binId() == BIN_ID_UNSET);
}
TEST_CASE("NestLargeItemShouldBeUntouched", "[Nesting]") {
TEST_CASE("LargeItemShouldBeUntouched", "[Nesting]") {
auto bin = Box(250000000, 210000000); // dummy bin
std::vector<Item> items;
items.emplace_back(RectangleItem{250000001, 210000001}); // Emplace large item
libnest2d::nest(items, bin);
size_t bins = libnest2d::nest(items, bin);
REQUIRE(bins == 0u);
REQUIRE(items.front().binId() == BIN_ID_UNSET);
}
TEST_CASE("Items can be preloaded", "[Nesting]") {
auto bin = Box({0, 0}, {250000000, 210000000}); // dummy bin
std::vector<Item> items;
items.reserve(2);
NestConfig<> cfg;
cfg.placer_config.alignment = NestConfig<>::Placement::Alignment::DONT_ALIGN;
items.emplace_back(RectangleItem{10000000, 10000000});
Item &fixed_rect = items.back();
fixed_rect.translate(bin.center());
items.emplace_back(RectangleItem{20000000, 20000000});
Item &movable_rect = items.back();
movable_rect.translate(bin.center());
SECTION("Preloaded Item should be untouched") {
fixed_rect.markAsFixedInBin(0);
size_t bins = libnest2d::nest(items, bin, 0, cfg);
REQUIRE(bins == 1);
REQUIRE(fixed_rect.binId() == 0);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
REQUIRE(movable_rect.binId() == 0);
REQUIRE(movable_rect.translation().X != bin.center().X);
REQUIRE(movable_rect.translation().Y != bin.center().Y);
}
SECTION("Preloaded Item should not affect free bins") {
fixed_rect.markAsFixedInBin(1);
size_t bins = libnest2d::nest(items, bin, 0, cfg);
REQUIRE(bins == 2);
REQUIRE(fixed_rect.binId() == 1);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
REQUIRE(movable_rect.binId() == 0);
auto bb = movable_rect.boundingBox();
REQUIRE(bb.center().X == bin.center().X);
REQUIRE(bb.center().Y == bin.center().Y);
}
}
namespace {
struct ItemPair {
@ -970,7 +1015,7 @@ TEST_CASE("mergePileWithPolygon", "[Geometry]") {
RectangleItem ref(45, 15);
REQUIRE(shapelike::area(result.front()) == ref.area());
REQUIRE(shapelike::area(result.front()) == Approx(ref.area()));
}
namespace {