Add libnest tests for various basic object functions
This commit is contained in:
parent
8c04536514
commit
69c02a407b
@ -982,6 +982,9 @@ template<class S> inline double area(const S& poly, const PolygonTag& )
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class RawShapes>
|
||||||
|
inline double area(const RawShapes& shapes, const MultiPolygonTag&);
|
||||||
|
|
||||||
template<class S> // Dispatching function
|
template<class S> // Dispatching function
|
||||||
inline double area(const S& sh)
|
inline double area(const S& sh)
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ using Coord = TCoord<PointImpl>;
|
|||||||
using Box = _Box<PointImpl>;
|
using Box = _Box<PointImpl>;
|
||||||
using Segment = _Segment<PointImpl>;
|
using Segment = _Segment<PointImpl>;
|
||||||
using Circle = _Circle<PointImpl>;
|
using Circle = _Circle<PointImpl>;
|
||||||
|
using MultiPolygon = TMultiShape<PolygonImpl>;
|
||||||
|
|
||||||
using Item = _Item<PolygonImpl>;
|
using Item = _Item<PolygonImpl>;
|
||||||
using Rectangle = _Rectangle<PolygonImpl>;
|
using Rectangle = _Rectangle<PolygonImpl>;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <libnest2d/libnest2d.hpp>
|
#include <libnest2d/nester.hpp>
|
||||||
|
|
||||||
namespace libnest2d { namespace svg {
|
namespace libnest2d { namespace svg {
|
||||||
|
|
||||||
@ -48,23 +48,28 @@ public:
|
|||||||
conf_.width = static_cast<double>(box.width()) /
|
conf_.width = static_cast<double>(box.width()) /
|
||||||
conf_.mm_in_coord_units;
|
conf_.mm_in_coord_units;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeItem(const Item& item) {
|
void writeShape(RawShape tsh) {
|
||||||
if(svg_layers_.empty()) addLayer();
|
if(svg_layers_.empty()) addLayer();
|
||||||
auto tsh = item.transformedShape();
|
|
||||||
if(conf_.origo_location == BOTTOMLEFT) {
|
if(conf_.origo_location == BOTTOMLEFT) {
|
||||||
auto d = static_cast<Coord>(
|
auto d = static_cast<Coord>(
|
||||||
std::round(conf_.height*conf_.mm_in_coord_units) );
|
std::round(conf_.height*conf_.mm_in_coord_units) );
|
||||||
|
|
||||||
auto& contour = shapelike::contour(tsh);
|
auto& contour = shapelike::contour(tsh);
|
||||||
for(auto& v : contour) setY(v, -getY(v) + d);
|
for(auto& v : contour) setY(v, -getY(v) + d);
|
||||||
|
|
||||||
auto& holes = shapelike::holes(tsh);
|
auto& holes = shapelike::holes(tsh);
|
||||||
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
|
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
|
||||||
|
|
||||||
}
|
}
|
||||||
currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
|
currentLayer() +=
|
||||||
1.0/conf_.mm_in_coord_units) + "\n";
|
shapelike::serialize<Formats::SVG>(tsh,
|
||||||
|
1.0 / conf_.mm_in_coord_units) +
|
||||||
|
"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeItem(const Item& item) {
|
||||||
|
writeShape(item.transformedShape());
|
||||||
}
|
}
|
||||||
|
|
||||||
void writePackGroup(const PackGroup& result) {
|
void writePackGroup(const PackGroup& result) {
|
||||||
|
@ -472,32 +472,30 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
|
|||||||
namespace {
|
namespace {
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
|
||||||
template<long long SCALE = 1, class Bin>
|
template<long long SCALE = 1, class It>
|
||||||
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin, int idx = 0) {
|
void exportSVG(const char *loc, It from, It to) {
|
||||||
std::string loc = "out";
|
|
||||||
|
|
||||||
static std::string svg_header =
|
static const char* svg_header =
|
||||||
R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
)raw";
|
)raw";
|
||||||
|
|
||||||
int i = idx;
|
|
||||||
auto r = result;
|
|
||||||
// for(auto r : result) {
|
// for(auto r : result) {
|
||||||
std::fstream out(loc + std::to_string(i) + ".svg", std::fstream::out);
|
std::fstream out(loc, std::fstream::out);
|
||||||
if(out.is_open()) {
|
if(out.is_open()) {
|
||||||
out << svg_header;
|
out << svg_header;
|
||||||
Item rbin( RectangleItem(bin.width(), bin.height()) );
|
// Item rbin( RectangleItem(bin.width(), bin.height()) );
|
||||||
for(unsigned j = 0; j < rbin.vertexCount(); j++) {
|
// for(unsigned j = 0; j < rbin.vertexCount(); j++) {
|
||||||
auto v = rbin.vertex(j);
|
// auto v = rbin.vertex(j);
|
||||||
setY(v, -getY(v)/SCALE + 500 );
|
// setY(v, -getY(v)/SCALE + 500 );
|
||||||
setX(v, getX(v)/SCALE);
|
// setX(v, getX(v)/SCALE);
|
||||||
rbin.setVertex(j, v);
|
// rbin.setVertex(j, v);
|
||||||
}
|
// }
|
||||||
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
// out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
||||||
for(Item& sh : r) {
|
for(auto it = from; it != to; ++it) {
|
||||||
Item tsh(sh.transformedShape());
|
const Item &itm = *it;
|
||||||
|
Item tsh(itm.transformedShape());
|
||||||
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
|
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
|
||||||
auto v = tsh.vertex(j);
|
auto v = tsh.vertex(j);
|
||||||
setY(v, -getY(v)/SCALE + 500);
|
setY(v, -getY(v)/SCALE + 500);
|
||||||
@ -509,10 +507,16 @@ void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin
|
|||||||
out << "\n</svg>" << std::endl;
|
out << "\n</svg>" << std::endl;
|
||||||
}
|
}
|
||||||
out.close();
|
out.close();
|
||||||
|
|
||||||
// i++;
|
// i++;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<long long SCALE = 1>
|
||||||
|
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, int idx = 0) {
|
||||||
|
exportSVG((std::string("out") + std::to_string(idx) + ".svg").c_str(),
|
||||||
|
result.begin(), result.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BottomLeftStressTest", "[Geometry]") {
|
TEST_CASE("BottomLeftStressTest", "[Geometry]") {
|
||||||
@ -541,7 +545,7 @@ TEST_CASE("BottomLeftStressTest", "[Geometry]") {
|
|||||||
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
valid = (valid && !r1.isInside(r2) && !r2.isInside(r1));
|
||||||
if(!valid) {
|
if(!valid) {
|
||||||
std::cout << "error index: " << i << std::endl;
|
std::cout << "error index: " << i << std::endl;
|
||||||
exportSVG(result, bin, i);
|
exportSVG<SCALE>(result, i);
|
||||||
}
|
}
|
||||||
REQUIRE(valid);
|
REQUIRE(valid);
|
||||||
} else {
|
} else {
|
||||||
@ -894,7 +898,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
|
|
||||||
int TEST_CASEcase = 0;
|
int TEST_CASEcase = 0;
|
||||||
|
|
||||||
auto& exportfun = exportSVG<SCALE, Box>;
|
auto& exportfun = exportSVG<SCALE>;
|
||||||
|
|
||||||
auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){
|
auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){
|
||||||
TEST_CASEcase++;
|
TEST_CASEcase++;
|
||||||
@ -941,7 +945,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||||||
std::ref(stationary), std::ref(tmp), std::ref(infp)
|
std::ref(stationary), std::ref(tmp), std::ref(infp)
|
||||||
};
|
};
|
||||||
|
|
||||||
exportfun(inp, bin, TEST_CASEcase*i++);
|
exportfun(inp, TEST_CASEcase*i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
REQUIRE(touching);
|
REQUIRE(touching);
|
||||||
@ -1096,3 +1100,91 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") {
|
|||||||
REQUIRE(succ);
|
REQUIRE(succ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class It> MultiPolygon merged_pile(It from, It to, int bin_id)
|
||||||
|
{
|
||||||
|
MultiPolygon pile;
|
||||||
|
pile.reserve(size_t(to - from));
|
||||||
|
|
||||||
|
for (auto it = from; it != to; ++it) {
|
||||||
|
if (it->binId() == bin_id) pile.emplace_back(it->transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
return nfp::merge(pile);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]")
|
||||||
|
{
|
||||||
|
static const constexpr ClipperLib::cInt W = 10000000;
|
||||||
|
|
||||||
|
// Get the input items and define the bin.
|
||||||
|
std::vector<RectangleItem> input(9, {W, W});
|
||||||
|
|
||||||
|
auto bin = Box::infinite();
|
||||||
|
|
||||||
|
NfpPlacer::Config pconfig;
|
||||||
|
|
||||||
|
pconfig.object_function = [](const Item &item) -> double {
|
||||||
|
return pl::magnsq<PointImpl, double>(item.boundingBox().center());
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t bins = nest(input, bin, 0, NestConfig{pconfig});
|
||||||
|
|
||||||
|
REQUIRE(bins == 1);
|
||||||
|
|
||||||
|
// Gather the items into piles of arranged polygons...
|
||||||
|
MultiPolygon pile;
|
||||||
|
pile.reserve(input.size());
|
||||||
|
|
||||||
|
for (auto &itm : input) {
|
||||||
|
REQUIRE(itm.binId() == 0);
|
||||||
|
pile.emplace_back(itm.transformedShape());
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiPolygon m = merged_pile(input.begin(), input.end(), 0);
|
||||||
|
|
||||||
|
REQUIRE(m.size() == 1);
|
||||||
|
|
||||||
|
REQUIRE(sl::area(m) == Approx(9. * W * W));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]")
|
||||||
|
{
|
||||||
|
static const constexpr ClipperLib::cInt W = 10000000;
|
||||||
|
static const constexpr size_t N = 100;
|
||||||
|
|
||||||
|
// Get the input items and define the bin.
|
||||||
|
std::vector<RectangleItem> input(N, {W, W});
|
||||||
|
|
||||||
|
auto bin = Box::infinite();
|
||||||
|
|
||||||
|
NfpPlacer::Config pconfig;
|
||||||
|
pconfig.rotations = {0.};
|
||||||
|
Box pile_box;
|
||||||
|
pconfig.before_packing =
|
||||||
|
[&pile_box](const MultiPolygon &pile,
|
||||||
|
const _ItemGroup<PolygonImpl> &/*packed_items*/,
|
||||||
|
const _ItemGroup<PolygonImpl> &/*remaining_items*/) {
|
||||||
|
pile_box = sl::boundingBox(pile);
|
||||||
|
};
|
||||||
|
|
||||||
|
pconfig.object_function = [&pile_box](const Item &item) -> double {
|
||||||
|
Box b = sl::boundingBox(item.boundingBox(), pile_box);
|
||||||
|
double area = b.area<double>() / (W * W);
|
||||||
|
return -area;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t bins = nest(input, bin, 0, NestConfig{pconfig});
|
||||||
|
|
||||||
|
// To debug:
|
||||||
|
exportSVG<1000000>("out", input.begin(), input.end());
|
||||||
|
|
||||||
|
REQUIRE(bins == 1);
|
||||||
|
|
||||||
|
MultiPolygon pile = merged_pile(input.begin(), input.end(), 0);
|
||||||
|
Box bb = sl::boundingBox(pile);
|
||||||
|
|
||||||
|
// Here the result shall be a stairway of boxes
|
||||||
|
REQUIRE(pile.size() == N);
|
||||||
|
REQUIRE(bb.area() == N * N * W * W);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user