Skip to content

Commit

Permalink
Minor tweaks to PointInPolygon benchmark testing.
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusJohnson committed Nov 29, 2023
1 parent 98db566 commit bc5855a
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 29 deletions.
125 changes: 97 additions & 28 deletions CPP/BenchMark/PointInPolygonBenchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using namespace Clipper2Lib;
using benchmark::State;

// PIP1: This is the current Clipper2 PointInPolygon code
template <typename T>
inline PointInPolygonResult PIP1(const Point<T>& pt, const Path<T>& polygon)
{
Expand Down Expand Up @@ -89,13 +90,18 @@ inline PointInPolygonResult PIP1(const Point<T>& pt, const Path<T>& polygon)
}


// PIP2: This is a not fully tested modification of the current
// Clipper2 PointInPolygon code. It's a little simpler and it's
// also marginally faster.
template <typename T>
inline PointInPolygonResult PIP2(const Point<T>& pt, const Path<T>& polygon)
{
if (!polygon.size()) return PointInPolygonResult::IsOutside;
Path<T>::const_iterator cend = polygon.cend();
Path<T>::const_iterator prev = cend - 1;
Path<T>::const_iterator curr = polygon.cbegin();
Path<T>::const_iterator last = cend - 1;
Path<T>::const_iterator first = polygon.cbegin();
Path<T>::const_iterator curr = first;
Path<T>::const_iterator prev = last;

bool is_above;
if (prev->y == pt.y)
Expand All @@ -115,21 +121,21 @@ inline PointInPolygonResult PIP2(const Point<T>& pt, const Path<T>& polygon)
{
if (is_above)
{
while (curr != cend && curr->y < pt.y) { prev = curr; ++curr; }
while (curr != cend && curr->y < pt.y) ++curr;
if (curr == cend) break;
}
else
{
while (curr != cend && curr->y > pt.y) { prev = curr; ++curr; }
while (curr != cend && curr->y > pt.y) ++curr;
if (curr == cend) break;
}

prev = (curr == first) ? last : curr - 1;
if (curr->y == pt.y)
{
if ((curr->x == pt.x) || ((curr->y == prev->y) &&
((pt.x > prev->x) == (pt.x < curr->x))))
return PointInPolygonResult::IsOn;
prev = curr;
++curr;
continue;
}
Expand All @@ -142,19 +148,20 @@ inline PointInPolygonResult PIP2(const Point<T>& pt, const Path<T>& polygon)
++val;
else
{
double d = CrossProduct(*prev, *curr, pt);
double d = CrossProduct(*prev, *curr, pt); //avoids integer overflow
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) ++val;
}
is_above = !is_above;
prev = curr;
++curr;
}

return (val % 2) ? PointInPolygonResult::IsInside : PointInPolygonResult::IsOutside;
return (val % 2) ?
PointInPolygonResult::IsInside :
PointInPolygonResult::IsOutside;
}


// PIP3: An entirely different algorithm for comparision.
// "Optimal Reliable Point-in-Polygon Test and
// Differential Coding Boolean Operations on Polygons"
// by Jianqiang Hao et al.
Expand Down Expand Up @@ -206,8 +213,10 @@ static PointInPolygonResult PIP3(const Point<T> &pt, const Path<T> &path)
}


// globals
Paths64 paths;
Point64 mp;
std::vector<PointInPolygonResult> pipResults;
PointInPolygonResult pip1 = PointInPolygonResult::IsOn;
PointInPolygonResult pip2 = PointInPolygonResult::IsOn;
PointInPolygonResult pip3 = PointInPolygonResult::IsOn;
Expand Down Expand Up @@ -242,17 +251,65 @@ static void CustomArguments(benchmark::internal::Benchmark* b)
for (int i = 0; i < paths.size(); ++i) b->Args({ i });
}

enum DoTests { do_stress_test_only, do_benchmark_only, do_all_tests };
enum ConsoleTextColor {
reset = 0,
//normal text colors ...
red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, white = 37,
//bold text colors ...
red_bold = 91, green_bold = 92, yellow_bold = 93, blue_bold = 94,
magenta_bold = 95, cyan_bold = 96, white_bold = 97
};

struct SetConsoleTextColor
{
private:
ConsoleTextColor _color;
public:
SetConsoleTextColor(ConsoleTextColor color) : _color(color) {};

static friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc)
{
return out << "\x1B[" << scc._color << "m";
}
};

static void DoErrorTest(int index)
{
PointInPolygonResult(*pip_func)(const Point64&, const Path64&);
switch (index)
{
case 1: pip_func = PIP1; break;
case 2: pip_func = PIP2; break;
case 3: pip_func = PIP2; break;
default: throw "oops! - wrong function!";
}
std::vector<size_t> errors;
std::cout << SetConsoleTextColor(green_bold) <<
"Testing PIP" << index << SetConsoleTextColor(reset) <<":";
for (size_t i = 0; i < paths.size(); ++i)
if (pip_func(mp, paths[i]) != pipResults[i]) errors.push_back(i);
if (errors.size())
{
size_t high_error = errors.size() - 1;
std::cout << SetConsoleTextColor(red_bold) << " Error in ";
for (size_t i = 0; i < high_error; ++i)
std::cout << errors[i] << " and ";
std::cout << errors[high_error] << "." <<
SetConsoleTextColor(reset) << std::endl;
}
else
std::cout << " No errors found." << std::endl;
}

int main(int argc, char** argv) {

const DoTests do_tests = do_all_tests;
enum DoTests { do_error_test_only, do_benchmark_only, do_all_tests };
const DoTests do_tests = do_all_tests;// do_error_test_only;//

if (do_tests != do_benchmark_only)
{
// stress test PIP2 with unusual polygons
mp = Point64(10, 10);
std::vector<PointInPolygonResult> pipResults;

paths.push_back({});
pipResults.push_back(PointInPolygonResult::IsOutside);
Expand All @@ -273,21 +330,12 @@ int main(int argc, char** argv) {
paths.push_back(MakePath({ 0,0, 20,20, 100,0 }));
pipResults.push_back(PointInPolygonResult::IsOn);

std::cout << "Stress testing PIP1 for errors: ";
for (size_t i = 0; i < paths.size(); ++i)
if (PIP1(mp, paths[i]) != pipResults[i])
std::cout << " (" << i << ")";
std::cout << std::endl;
std::cout << "Stress testing PIP2 for errors: ";
for (size_t i = 0; i < paths.size(); ++i)
if (PIP2(mp, paths[i]) != pipResults[i])
std::cout << " (" << i << ")";
std::cout << "Error Tests:" << std::endl << std::endl;

DoErrorTest(1);
DoErrorTest(2);
DoErrorTest(3);
std::cout << std::endl;
std::cout << "Stress testing PIP3 for errors: ";
for (size_t i = 0; i < paths.size(); ++i)
if (PIP3(mp, paths[i]) != pipResults[i])
std::cout << " (" << i << ")";
std::cout << std::endl << std::endl;

if (do_tests != do_all_tests)
{
Expand All @@ -297,21 +345,42 @@ int main(int argc, char** argv) {
}
}

if (do_tests == do_stress_test_only) return 0;
if (do_tests == do_error_test_only) return 0;

std::cout << "Benchmarks 1: " <<
"benchmarking PIP on a single elliptical path" << std::endl << std::endl;

// compare 3 PIP algorithms
const int width = 600000, height = 400000;
mp = Point64(width / 2, height / 2);
paths.clear();
paths.push_back(Ellipse(mp, 10000, 6000, 10000));
std::vector<int64_t> args{ 0 };
benchmark::Initialize(&argc, argv);
BENCHMARK(BM_PIP1)->Args(args); // current Clipper2
BENCHMARK(BM_PIP2)->Args(args); // modified Clipper2
BENCHMARK(BM_PIP3)->Args(args); // Hao et al. (2018)
benchmark::RunSpecifiedBenchmarks(benchmark::CreateDefaultDisplayReporter());
benchmark::ClearRegisteredBenchmarks();
benchmark::Shutdown();

std::cout << std::endl << std::endl <<
"Setting up before Benchmarks 2 ..." <<
std::endl << std::endl;

paths.clear();
srand((unsigned)time(0));
for (int i = 0, count = 10000; i < 5; ++i, count *= 10)
paths.push_back(MakeRandomPoly(width, height, count));

std::cout << "Benchmarks 2: " <<
"benchmarking PIP using a single self-intersecting polygon" << std::endl << std::endl;

benchmark::Initialize(&argc, argv);
BENCHMARK(BM_PIP1)->Apply(CustomArguments); // current Clipper2
BENCHMARK(BM_PIP2)->Apply(CustomArguments); // modified Clipper2
BENCHMARK(BM_PIP3)->Apply(CustomArguments); // Hao et al. (2018)
benchmark::RunSpecifiedBenchmarks();
benchmark::RunSpecifiedBenchmarks(benchmark::CreateDefaultDisplayReporter());

if (pip2 != pip1 || pip3 != pip1)
{
Expand Down
2 changes: 1 addition & 1 deletion CPP/Clipper2Lib/include/clipper2/clipper.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ namespace Clipper2Lib {
result.reserve(array_size / 2);
for (size_t i = 0; i < array_size; i +=2)
#ifdef USINGZ
result.push_back( U{ an_array[i], an_array[i +1], 0} );
result.push_back( U{ an_array[i], an_array[i + 1], 0} );
#else
result.push_back( U{ an_array[i], an_array[i + 1]} );
#endif
Expand Down

0 comments on commit bc5855a

Please sign in to comment.