You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

426 lines
15 KiB
C++

//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "PolygonUtilsTest.h"
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(PolygonUtilsTest);
void PolygonUtilsTest::setUp()
{
test_square.emplace_back(0, 0);
test_square.emplace_back(100, 0);
test_square.emplace_back(100, 100);
test_square.emplace_back(0, 100);
test_squares.add(test_square);
pointy_square.emplace_back(0, 0);
pointy_square.emplace_back(47, 0);
pointy_square.emplace_back(50, 80);
pointy_square.emplace_back(53, 0);
pointy_square.emplace_back(100, 0);
pointy_square.emplace_back(100, 100);
pointy_square.emplace_back(55, 100);
pointy_square.emplace_back(50, 180);
pointy_square.emplace_back(45, 100);
pointy_square.emplace_back(0, 100);
}
void PolygonUtilsTest::tearDown()
{
//Do nothing.
}
void PolygonUtilsTest::cornerInsideTest()
{
moveInsideAssert(test_square, Point(110, 110), 28, Point(80, 80));
}
void PolygonUtilsTest::edgeInsideTest()
{
moveInsideAssert(test_square, Point(50, 110), 20, Point(50, 80));
}
void PolygonUtilsTest::cornerOutsideTest()
{
moveInsideAssert(test_square, Point(110, 110), -28, Point(120, 120));
}
void PolygonUtilsTest::edgeOutsideTest()
{
moveInsideAssert(test_square, Point(50, 110), -20, Point(50, 120));
}
void PolygonUtilsTest::cornerCrookedTest()
{
moveInsideAssert(test_square, Point(110, 105), 28, Point(80, 80));
}
void PolygonUtilsTest::cornerEdgeTest()
{
const Point close_to(110, 100);
const Point supposed1(80, 80); //Allow two possible values here, since the behaviour for this edge case is not specified.
const Point supposed2(72, 100);
const int distance = 28;
const ClosestPolygonPoint cpp = PolygonUtils::findClosest(close_to, test_square);
const Point result = PolygonUtils::moveInside(cpp, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed1 << " or " << supposed2 << ".\n";
ss << "\tPS: dist to boundary computed = " << vSize(cpp.location - result) << "; idem supposed = " << vSize(cpp.location - result) << ".\n";
ss << "\tclosest point = " << cpp.location << " at index " << cpp.point_idx << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed1) <= maximum_error || vSize(result - supposed2) <= maximum_error);
}
}
void PolygonUtilsTest::onBorderTest()
{
moveInsideAssert(test_square, Point(100, 50), 20, Point(80, 50));
}
void PolygonUtilsTest::insideTest()
{
moveInsideAssert(test_square, Point(80, 50), 20, Point(80, 50));
}
void PolygonUtilsTest::middleTest()
{
const Point close_to(50, 50);
const Point supposed1(80, 50); //Allow four possible values here, since the behaviour for this edge case is not specified.
const Point supposed2(50, 80);
const Point supposed3(20, 50);
const Point supposed4(50, 20);
const int distance = 20;
const ClosestPolygonPoint cpp = PolygonUtils::findClosest(close_to, test_square);
const Point result = PolygonUtils::moveInside(cpp, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed1 << ", " << supposed2 << ", " << supposed3 << " or " << supposed4 << ".\n";
ss << "\tPS: dist to boundary computed = " << vSize(cpp.location - result) << "; idem supposed = " << vSize(cpp.location - result) << ".\n";
ss << "\tclosest point = " << cpp.location << " at index " << cpp.point_idx << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed1) <= maximum_error || vSize(result - supposed2) <= maximum_error || vSize(result - supposed3) <= maximum_error || vSize(result - supposed4) <= maximum_error);
}
}
void PolygonUtilsTest::middleTestPenalty()
{
const Point close_to(50, 50);
const Point supposed(80, 50);
const Point preferred_dir(120, 60);
const int distance = 20;
const ClosestPolygonPoint cpp = PolygonUtils::findClosest(close_to, test_square, [preferred_dir](Point candidate) { return vSize2(candidate - preferred_dir); } );
const Point result = PolygonUtils::moveInside(cpp, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed << ".\n";
ss << "\tPS: dist to boundary computed = " << vSize(cpp.location - result) << "; idem supposed = " << vSize(cpp.location - result) << ".\n";
ss << "\tclosest point = " << cpp.location << " at index " << cpp.point_idx << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) <= maximum_error);
}
}
void PolygonUtilsTest::noMoveTest()
{
moveInsideAssert(test_square, Point(110, 50), 0, Point(100, 50));
}
void PolygonUtilsTest::farMoveTest()
{
moveInsideAssert(test_square, Point(110, 50), 100000, Point(-99900, 50));
}
void PolygonUtilsTest::cornerInsideTest2()
{
moveInside2Assert(test_square, Point(110, 110), 28, Point(80, 80));
}
void PolygonUtilsTest::edgeInsideTest2()
{
moveInside2Assert(test_square, Point(50, 110), 20, Point(50, 80));
}
void PolygonUtilsTest::cornerOutsideTest2()
{
moveInside2Assert(test_square, Point(110, 110), -28, Point(120, 120));
}
void PolygonUtilsTest::edgeOutsideTest2()
{
moveInside2Assert(test_square, Point(50, 110), -20, Point(50, 120));
}
void PolygonUtilsTest::cornerCrookedTest2()
{
moveInside2Assert(test_square, Point(110, 105), 28, Point(80, 80));
}
void PolygonUtilsTest::cornerEdgeTest2()
{
const Point close_to(110, 100);
const Point supposed1(80, 80); //Allow two possible values here, since the behaviour for this edge case is not specified.
const Point supposed2(72, 100);
const int distance = 28;
Polygons polys;
polys.add(test_square);
Point result = close_to;
PolygonUtils::moveInside2(polys, result, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed1 << " or " << supposed2 << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed1) <= maximum_error || vSize(result - supposed2) <= maximum_error);
}
}
void PolygonUtilsTest::onBorderTest2()
{
moveInside2Assert(test_square, Point(100, 50), 20, Point(80, 50));
}
void PolygonUtilsTest::insideTest2()
{
moveInside2Assert(test_square, Point(80, 50), 20, Point(80, 50));
}
void PolygonUtilsTest::middleTest2()
{
moveInside2Assert(test_square, Point(50, 50), 20, Point(50, 50)); // this moveInside function leaves points which are already inside as they are
}
void PolygonUtilsTest::noMoveTest2()
{
moveInside2Assert(test_square, Point(110, 50), 0, Point(100, 50));
}
void PolygonUtilsTest::farMoveTest2()
{
moveInside2Assert(test_square, Point(110, 50), 100000, Point(-99900, 50));
}
void PolygonUtilsTest::moveInsideAssert(const PolygonRef poly, Point close_to, const int distance, Point supposed)
{
ClosestPolygonPoint cpp = PolygonUtils::findClosest(close_to, poly);
Point result = PolygonUtils::moveInside(cpp, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed << ".\n";
ss << "\tPS: dist to boundary computed = " << vSize(cpp.location - result) << "; idem supposed = " << vSize(cpp.location - result) << ".\n";
ss << "\tclosest point = " << cpp.location << " at index " << cpp.point_idx << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) <= maximum_error);
}
}
void PolygonUtilsTest::moveInside2Assert(const PolygonRef poly, Point close_to, const int distance, Point supposed)
{
Polygons polys;
polys.add(poly);
Point result = close_to;
PolygonUtils::moveInside2(polys, result, distance);
{
std::stringstream ss;
ss << close_to << " moved with " << distance << " micron inside to " << result << " rather than " << supposed << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) <= maximum_error);
}
}
void PolygonUtilsTest::cornerFindCloseTest()
{
findCloseAssert(test_square, Point(110,110), Point(100,100), 15);
}
void PolygonUtilsTest::edgeFindCloseTest()
{
findCloseAssert(test_square, Point(50,110), Point(50,100), 15);
}
void PolygonUtilsTest::middleEdgeFindCloseTest()
{
std::function<int(Point)> penalty_function([](Point candidate){ return -vSize2(candidate - Point(50,100)); }); // further from 50,100 is less penalty
findCloseAssert(test_square, Point(50,50), Point(50,0), 60, &penalty_function);
}
void PolygonUtilsTest::findCloseAssert(const PolygonRef poly, Point close_to, Point supposed, int cell_size, const std::function<int(Point)>* penalty_function)
{
Polygons polys;
polys.add(poly);
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* loc_to_line = PolygonUtils::createLocToLineGrid(polys, cell_size);
std::optional<ClosestPolygonPoint> cpp;
if (penalty_function)
{
cpp = PolygonUtils::findClose(close_to, polys, *loc_to_line, *penalty_function);
}
else
{
cpp = PolygonUtils::findClose(close_to, polys, *loc_to_line);
}
if (cpp)
{
std::stringstream ss;
Point result = cpp->location;
ss << "Close to " << close_to << " we found " << result << " rather than " << supposed << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) <= maximum_error);
}
else
{
std::stringstream ss;
ss << "Couldn't find anything close to " << close_to << " ( should have been " << supposed << ").\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), false);
}
delete loc_to_line;
}
void PolygonUtilsTest::moveInsidePointyCornerTest()
{
Point from(55, 170); // above pointy bit
Point result(from);
Polygons inside;
inside.add(pointy_square);
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(inside, result, 10);
if (cpp.point_idx == NO_INDEX || cpp.poly_idx == NO_INDEX)
{
std::stringstream ss;
ss << "Couldn't ensure point inside close to " << from << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), false);
}
else
{
std::stringstream ss;
ss << from << " couldn't be moved inside.\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), inside.inside(result));
}
}
void PolygonUtilsTest::moveInsidePointyCornerTestFail()
{ // should fail with normal moveInside2 (and the like)
Point from(55, 170); // above pointy bit
Point result(from);
Polygons inside;
inside.add(pointy_square);
ClosestPolygonPoint cpp = PolygonUtils::moveInside2(inside, result, 10);
if (cpp.point_idx == NO_INDEX || cpp.poly_idx == NO_INDEX)
{
std::stringstream ss;
ss << "Couldn't ensure point inside close to " << from << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), false);
}
else
{
std::stringstream ss;
ss << from << " could be moved inside, while it was designed to fail.\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), !inside.inside(result));
}
}
void PolygonUtilsTest::moveOutsidePointyCornerTest()
{
Point from(60, 70); // above pointy bit
Point result(from);
Point supposed(50, 70); // 10 below pointy bit
Polygons inside;
inside.add(pointy_square);
// ClosestPolygonPoint cpp = PolygonUtils::moveInside2(inside, result, -10);
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(inside, result, -10);
if (cpp.point_idx == NO_INDEX || cpp.poly_idx == NO_INDEX)
{
std::stringstream ss;
ss << "Couldn't ensure point inside close to " << from << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), false);
}
else
{
std::stringstream ss;
ss << from << " couldn't be moved inside.\n";
// CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) < 5 + maximum_error && !inside.inside(result)); // +5 because ensureInside might do half the preferred distance moved inside
CPPUNIT_ASSERT_MESSAGE(ss.str(), !inside.inside(result)); // +5 because ensureInside might do half the preferred distance moved inside
}
}
void PolygonUtilsTest::moveOutsidePointyCornerTestFail()
{ // should fail with normal moveInside2 (and the like)
Point from(60, 70); // above pointy bit
Point result(from);
Point supposed(50, 70); // 10 below pointy bit
Polygons inside;
inside.add(pointy_square);
ClosestPolygonPoint cpp = PolygonUtils::moveInside2(inside, result, -10);
// ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(inside, result, -10);
if (cpp.point_idx == NO_INDEX || cpp.poly_idx == NO_INDEX)
{
std::stringstream ss;
ss << "Couldn't ensure point inside close to " << from << ".\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), false);
}
else
{
std::stringstream ss;
ss << from << " could be moved inside to " << result << ", while it was designed to fail.\n";
// CPPUNIT_ASSERT_MESSAGE(ss.str(), vSize(result - supposed) < 5 + maximum_error && !inside.inside(result)); // +5 because ensureInside might do half the preferred distance moved inside
CPPUNIT_ASSERT_MESSAGE(ss.str(), inside.inside(result)); // +5 because ensureInside might do half the preferred distance moved inside
}
}
void PolygonUtilsTest::spreadDotsTestSegment()
{
std::vector<ClosestPolygonPoint> supposed;
supposed.emplace_back(Point(50, 0), 0, test_squares[0], 0);
supposed.emplace_back(Point(100, 0), 1, test_squares[0], 0);
supposed.emplace_back(Point(100, 50), 1, test_squares[0], 0);
spreadDotsAssert(PolygonsPointIndex(&test_squares, 0, 0), PolygonsPointIndex(&test_squares, 0, 2), 3, supposed);
}
void PolygonUtilsTest::spreadDotsTestFull()
{
std::vector<ClosestPolygonPoint> supposed;
supposed.emplace_back(Point(0, 0), 0, test_squares[0], 0);
supposed.emplace_back(Point(50, 0), 0, test_squares[0], 0);
supposed.emplace_back(Point(100, 0), 1, test_squares[0], 0);
supposed.emplace_back(Point(100, 50), 1, test_squares[0], 0);
supposed.emplace_back(Point(100, 100), 2, test_squares[0], 0);
supposed.emplace_back(Point(50, 100), 2, test_squares[0], 0);
supposed.emplace_back(Point(0, 100), 3, test_squares[0], 0);
supposed.emplace_back(Point(0, 50), 3, test_squares[0], 0);
spreadDotsAssert(PolygonsPointIndex(&test_squares, 0, 0), PolygonsPointIndex(&test_squares, 0, 0), 8, supposed);
}
void PolygonUtilsTest::spreadDotsAssert(PolygonsPointIndex start, PolygonsPointIndex end, unsigned int n_dots, const std::vector<ClosestPolygonPoint>& supposed)
{
std::vector<ClosestPolygonPoint> result;
PolygonUtils::spreadDots(start, end, n_dots, result);
std::stringstream ss;
ss << "PolygonUtils::spreadDots(" << start.point_idx << ", " << end.point_idx << ", " << n_dots << ") generated " << result.size() << " points, rather than " << supposed.size() << "!\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), result.size() == supposed.size());
for (unsigned int point_idx = 0 ; point_idx < result.size(); point_idx++)
{
std::stringstream ss;
ss << point_idx << "nd point of PolygonUtils::spreadDots(" << start.point_idx << ", " << end.point_idx << ", " << n_dots << ") was " << result[point_idx].p() << ", rather than " << supposed[point_idx].p() << "!\n";
CPPUNIT_ASSERT_MESSAGE(ss.str(), result[point_idx].p() == supposed[point_idx].p());
}
}
}