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.
525 lines
16 KiB
C++
525 lines
16 KiB
C++
|
1 month ago
|
#include "benchmark/benchmark.h"
|
||
|
|
#include "clipper2/clipper.h"
|
||
|
|
#include "CommonUtils.h"
|
||
|
|
#include "ClipFileLoad.h"
|
||
|
|
#include <iostream>
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <ctime>
|
||
|
|
|
||
|
|
using namespace Clipper2Lib;
|
||
|
|
|
||
|
|
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
|
||
|
|
};
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// SetConsoleTextColor: a simple class to adjust Console Text Colors (Windows & Linux)
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
struct SetConsoleTextColor
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
ConsoleTextColor _color;
|
||
|
|
public:
|
||
|
|
SetConsoleTextColor(ConsoleTextColor color) : _color(color) {};
|
||
|
|
|
||
|
|
friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc)
|
||
|
|
{
|
||
|
|
return out << "\x1B[" << scc._color << "m";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
|
||
|
|
typedef std::function<PointInPolygonResult(const Point64&, const Path64&)> PipFunction;
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// PIP1: This is the current Clipper2 PointInPolygon code
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
inline PointInPolygonResult PIP1(const Point64& pt, const Path64& polygon)
|
||
|
|
{
|
||
|
|
int val = 0;
|
||
|
|
typename Path64::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev;
|
||
|
|
typename Path64::const_iterator cend = polygon.cend();
|
||
|
|
|
||
|
|
while (first != cend && first->y == pt.y) ++first;
|
||
|
|
if (first == cend) // not a proper polygon
|
||
|
|
return PointInPolygonResult::IsOutside;
|
||
|
|
|
||
|
|
bool is_above = first->y < pt.y, starting_above = is_above;
|
||
|
|
curr = first + 1;
|
||
|
|
while (true)
|
||
|
|
{
|
||
|
|
if (curr == cend)
|
||
|
|
{
|
||
|
|
if (cend == first || first == cbegin) break;
|
||
|
|
cend = first;
|
||
|
|
curr = cbegin;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_above)
|
||
|
|
{
|
||
|
|
while (curr != cend && curr->y < pt.y) ++curr;
|
||
|
|
if (curr == cend) continue;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
while (curr != cend && curr->y > pt.y) ++curr;
|
||
|
|
if (curr == cend) continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (curr == cbegin)
|
||
|
|
prev = polygon.cend() - 1;
|
||
|
|
else
|
||
|
|
prev = 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;
|
||
|
|
++curr;
|
||
|
|
if (curr == first) break;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pt.x < curr->x && pt.x < prev->x)
|
||
|
|
{
|
||
|
|
// we're only interested in edges crossing on the left
|
||
|
|
}
|
||
|
|
else if (pt.x > prev->x && pt.x > curr->x)
|
||
|
|
val = 1 - val; // toggle val
|
||
|
|
else
|
||
|
|
{
|
||
|
|
double d = CrossProduct(*prev, *curr, pt);
|
||
|
|
if (d == 0) return PointInPolygonResult::IsOn;
|
||
|
|
if ((d < 0) == is_above) val = 1 - val;
|
||
|
|
}
|
||
|
|
is_above = !is_above;
|
||
|
|
++curr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_above != starting_above)
|
||
|
|
{
|
||
|
|
cend = polygon.cend();
|
||
|
|
if (curr == cend) curr = cbegin;
|
||
|
|
if (curr == cbegin) prev = cend - 1;
|
||
|
|
else prev = curr - 1;
|
||
|
|
double d = CrossProduct(*prev, *curr, pt);
|
||
|
|
if (d == 0) return PointInPolygonResult::IsOn;
|
||
|
|
if ((d < 0) == is_above) val = 1 - val;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (val == 0) ?
|
||
|
|
PointInPolygonResult::IsOutside :
|
||
|
|
PointInPolygonResult::IsInside;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// PIP2: This is a not fully tested modification of the
|
||
|
|
// current Clipper2 PointInPolygon code. It's a little
|
||
|
|
// simpler and also marginally faster.
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
inline PointInPolygonResult PIP2(const Point64& pt, const Path64& polygon)
|
||
|
|
{
|
||
|
|
if (!polygon.size()) return PointInPolygonResult::IsOutside;
|
||
|
|
Path64::const_iterator cend = polygon.cend();
|
||
|
|
Path64::const_iterator last = cend - 1;
|
||
|
|
Path64::const_iterator first = polygon.cbegin();
|
||
|
|
Path64::const_iterator curr = first;
|
||
|
|
Path64::const_iterator prev = last;
|
||
|
|
|
||
|
|
bool is_above;
|
||
|
|
if (prev->y == pt.y)
|
||
|
|
{
|
||
|
|
if (pt == *prev) return PointInPolygonResult::IsOn;
|
||
|
|
if ((curr->y == pt.y) && ((curr->x == pt.x) ||
|
||
|
|
((pt.x > prev->x) == (pt.x < curr->x))))
|
||
|
|
return PointInPolygonResult::IsOn;
|
||
|
|
Path64::const_reverse_iterator pr = polygon.crbegin() +1;
|
||
|
|
while (pr != polygon.crend() && pr->y == pt.y) ++pr;
|
||
|
|
is_above = pr == polygon.crend() || pr->y < pt.y;
|
||
|
|
}
|
||
|
|
else is_above = prev->y < pt.y;
|
||
|
|
|
||
|
|
int val = 0;
|
||
|
|
while (curr != cend)
|
||
|
|
{
|
||
|
|
if (is_above)
|
||
|
|
{
|
||
|
|
while (curr != cend && curr->y < pt.y) ++curr;
|
||
|
|
if (curr == cend) break;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
++curr;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pt.x < curr->x && pt.x < prev->x)
|
||
|
|
{
|
||
|
|
// we're only interested in edges crossing on the left
|
||
|
|
}
|
||
|
|
else if (pt.x > prev->x && pt.x > curr->x)
|
||
|
|
++val;
|
||
|
|
else
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
++curr;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (val % 2) ?
|
||
|
|
PointInPolygonResult::IsInside :
|
||
|
|
PointInPolygonResult::IsOutside;
|
||
|
|
}
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// PIP3: An entirely different algorithm for comparison.
|
||
|
|
// "Optimal Reliable Point-in-Polygon Test and
|
||
|
|
// Differential Coding Boolean Operations on Polygons"
|
||
|
|
// by Jianqiang Hao et al.
|
||
|
|
// Symmetry 2018, 10(10), 477; https://doi.org/10.3390/sym10100477
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
static PointInPolygonResult PIP3(const Point64&pt, const Path64&path)
|
||
|
|
{
|
||
|
|
if (!path.size()) return PointInPolygonResult::IsOutside;
|
||
|
|
int64_t x1, y1, x2, y2;
|
||
|
|
int k = 0;
|
||
|
|
Path64::const_iterator itPrev = path.cend() - 1;
|
||
|
|
Path64::const_iterator itCurr = path.cbegin();
|
||
|
|
for ( ; itCurr != path.cend(); ++itCurr)
|
||
|
|
{
|
||
|
|
y1 = itPrev->y - pt.y;
|
||
|
|
y2 = itCurr->y - pt.y;
|
||
|
|
if (((y1 < 0) && (y2 < 0)) || ((y1 > 0) && (y2 > 0)))
|
||
|
|
{
|
||
|
|
itPrev = itCurr;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
x1 = itPrev->x - pt.x;
|
||
|
|
x2 = itCurr->x - pt.x;
|
||
|
|
if ((y1 <= 0) && (y2 > 0))
|
||
|
|
{
|
||
|
|
//double f = double(x1) * y2 - double(x2) * y1; // avoids int overflow
|
||
|
|
int64_t f = x1 * y2 - x2 * y1;
|
||
|
|
if (f > 0) ++k;
|
||
|
|
else if (f == 0) return PointInPolygonResult::IsOn;
|
||
|
|
}
|
||
|
|
else if ((y1 > 0) && (y2 <= 0))
|
||
|
|
{
|
||
|
|
int64_t f = x1 * y2 - x2 * y1;
|
||
|
|
if (f < 0) ++k;
|
||
|
|
else if (f == 0) return PointInPolygonResult::IsOn;
|
||
|
|
}
|
||
|
|
else if (((y2 == 0) && (y1 < 0)) || ((y1 == 0) && (y2 < 0)))
|
||
|
|
{
|
||
|
|
int64_t f = x1 * y2 - x2 * y1;
|
||
|
|
if (f == 0) return PointInPolygonResult::IsOn;
|
||
|
|
}
|
||
|
|
else if ((y1 == 0) && (y2 == 0) &&
|
||
|
|
(((x2 <= 0) && (x1 >= 0)) || ((x1 <= 0) && (x2 >= 0))))
|
||
|
|
return PointInPolygonResult::IsOn;
|
||
|
|
itPrev = itCurr;
|
||
|
|
}
|
||
|
|
if (k % 2) return PointInPolygonResult::IsInside;
|
||
|
|
return PointInPolygonResult::IsOutside;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// global data structures
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
const Path64 points_of_interest_outside =
|
||
|
|
MakePath({ 21887,10420, 21726,10825, 21662,10845, 21617,10890 });
|
||
|
|
const Path64 points_of_interest_inside =
|
||
|
|
MakePath({ 21887,10430, 21843,10520, 21810,10686, 21900,10461 });
|
||
|
|
|
||
|
|
Point64 mp;
|
||
|
|
Paths64 paths;
|
||
|
|
std::vector < std::vector<PointInPolygonResult> > pipResults;
|
||
|
|
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// Benchmark callback functions
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
static void BM_PIP1(benchmark::State& state)
|
||
|
|
{
|
||
|
|
int64_t idx = state.range(0);
|
||
|
|
for (auto _ : state)
|
||
|
|
pipResults[0][idx] = PIP1(mp, paths[idx]);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static void BM_PIP2(benchmark::State& state)
|
||
|
|
{
|
||
|
|
int64_t idx = state.range(0);
|
||
|
|
for (auto _ : state)
|
||
|
|
pipResults[1][idx] = PIP2(mp, paths[idx]);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void BM_PIP3(benchmark::State& state)
|
||
|
|
{
|
||
|
|
int64_t idx = state.range(0);
|
||
|
|
for (auto _ : state)
|
||
|
|
pipResults[2][idx] = PIP3(mp, paths[idx]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// Miscellaneous functions
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
static void CustomArguments(benchmark::internal::Benchmark* b)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < paths.size(); ++i) b->Args({ i });
|
||
|
|
}
|
||
|
|
|
||
|
|
inline PipFunction GetPIPFunc(int index)
|
||
|
|
{
|
||
|
|
PipFunction result;
|
||
|
|
switch (index)
|
||
|
|
{
|
||
|
|
case 0: result = PIP1; break;
|
||
|
|
case 1: result = PIP2; break;
|
||
|
|
case 2: result = PIP3; break;
|
||
|
|
default: throw "oops! - wrong function!";
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// Error checking functions
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
static void DoErrorTest1_internal(const Path64& pts_of_int, const Paths64& paths,
|
||
|
|
PipFunction pip_func, PointInPolygonResult expected)
|
||
|
|
{
|
||
|
|
Path64 error_points;
|
||
|
|
|
||
|
|
for (Point64 poi : pts_of_int)
|
||
|
|
{
|
||
|
|
size_t inside_cnt = 0;
|
||
|
|
for (const Path64& path : paths)
|
||
|
|
if (pip_func(poi, path) == PointInPolygonResult::IsInside) ++inside_cnt;
|
||
|
|
switch (expected)
|
||
|
|
{
|
||
|
|
case PointInPolygonResult::IsInside:
|
||
|
|
if (inside_cnt != 1) error_points.push_back(poi); break;
|
||
|
|
case PointInPolygonResult::IsOutside:
|
||
|
|
if (inside_cnt) error_points.push_back(poi); break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (error_points.size())
|
||
|
|
{
|
||
|
|
size_t high_error = error_points.size() - 1;
|
||
|
|
std::cout << SetConsoleTextColor(red_bold) << " Errors at ";
|
||
|
|
for (size_t i = 0; i < high_error; ++i) std::cout << "(" << error_points[i] << "), ";
|
||
|
|
std::cout << "(" << error_points[high_error] << ")." << SetConsoleTextColor(reset) << std::endl;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
std::cout << " No errors found." << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void DoErrorTest1(int index)
|
||
|
|
{
|
||
|
|
PipFunction pip_func = GetPIPFunc(index);
|
||
|
|
|
||
|
|
std::cout << SetConsoleTextColor(green_bold) <<
|
||
|
|
"Testing PIP" << index +1 << "/outside:" << SetConsoleTextColor(reset);
|
||
|
|
DoErrorTest1_internal(points_of_interest_outside, paths,
|
||
|
|
pip_func, PointInPolygonResult::IsOutside);
|
||
|
|
|
||
|
|
std::cout << SetConsoleTextColor(green_bold) <<
|
||
|
|
"Testing PIP" << index +1 << "/inside :" << SetConsoleTextColor(reset);
|
||
|
|
DoErrorTest1_internal(points_of_interest_inside, paths,
|
||
|
|
pip_func, PointInPolygonResult::IsInside);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void DoErrorTest2(int index)
|
||
|
|
{
|
||
|
|
PipFunction pip_func = GetPIPFunc(index);
|
||
|
|
|
||
|
|
std::vector<size_t> errors;
|
||
|
|
std::cout << SetConsoleTextColor(green_bold) <<
|
||
|
|
"Testing PIP" << index +1 << SetConsoleTextColor(reset) <<":";
|
||
|
|
for (size_t i = 0; i < paths.size(); ++i)
|
||
|
|
if (pip_func(mp, paths[i]) != pipResults[0][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;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
// Main Entry
|
||
|
|
/////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
int main(int argc, char** argv)
|
||
|
|
{
|
||
|
|
std::cout << SetConsoleTextColor(cyan_bold) <<
|
||
|
|
"Simple error checks ..." << SetConsoleTextColor(reset) <<
|
||
|
|
std::endl;
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////
|
||
|
|
// 1. Very basic error testing
|
||
|
|
//////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
std::cout << std::endl << SetConsoleTextColor(yellow_bold) <<
|
||
|
|
"Tests for errors #1:" << SetConsoleTextColor(reset) << std::endl <<
|
||
|
|
"(Reusing 'TestPolytreeHoles' tests)" << std::endl << std::endl;
|
||
|
|
|
||
|
|
// 1a. use const paths (PolytreeHoleOwner2.txt) with changing points of interest
|
||
|
|
|
||
|
|
Paths64 subject, subject_open, clip;
|
||
|
|
int64_t _, __;
|
||
|
|
ClipType ___;
|
||
|
|
FillRule ____;
|
||
|
|
const std::string test_file = "../../../../../Tests/PolytreeHoleOwner2.txt";
|
||
|
|
if (!FileExists(test_file)) return 1;
|
||
|
|
std::ifstream ifs(test_file);
|
||
|
|
if (!ifs || !ifs.good()) return 1;
|
||
|
|
LoadTestNum(ifs, 1, paths, subject_open, clip, _, __, ___, ____);
|
||
|
|
ifs.close();
|
||
|
|
|
||
|
|
for (int i = 0; i < 3; ++i) DoErrorTest1(i);
|
||
|
|
|
||
|
|
// 1b. Use a const point of interest (10,10) against various paths
|
||
|
|
|
||
|
|
std::cout << std::endl << SetConsoleTextColor(yellow_bold) <<
|
||
|
|
"Tests for errors #2:" << SetConsoleTextColor(reset) << std::endl <<
|
||
|
|
"(Testing with 'unusual' polygons)" << std::endl << std::endl;
|
||
|
|
|
||
|
|
mp = Point64(10, 10);
|
||
|
|
paths.clear();
|
||
|
|
pipResults.clear();
|
||
|
|
pipResults.resize(1);
|
||
|
|
paths.push_back({}); // ie test an empty path
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOutside);
|
||
|
|
paths.push_back(MakePath({ 100,10, 200,10 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOutside);
|
||
|
|
paths.push_back(MakePath({ 100,10, 200,10, 10,10, 20,20 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOn);
|
||
|
|
paths.push_back(MakePath({ 10,10 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOn);
|
||
|
|
paths.push_back(MakePath({ 100,10 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOutside);
|
||
|
|
paths.push_back(MakePath({ 100,10, 110,20, 200,10, 10,10, 20,20 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOn);
|
||
|
|
paths.push_back(MakePath({ 100,10, 110,20, 200,10, 20,20 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOutside);
|
||
|
|
paths.push_back(MakePath({ 200,0, 0,0, 10,20, 200,0, 20,0 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsInside);
|
||
|
|
paths.push_back(MakePath({ 0,0, 20,20, 100,0 }));
|
||
|
|
pipResults[0].push_back(PointInPolygonResult::IsOn);
|
||
|
|
|
||
|
|
for (int i = 0; i < 3; ++i) DoErrorTest2(i);
|
||
|
|
std::cout << std::endl;
|
||
|
|
|
||
|
|
// 2. Benchmark functions
|
||
|
|
|
||
|
|
std::cout << std::endl << SetConsoleTextColor(cyan_bold) <<
|
||
|
|
"Benchmarking ..." << SetConsoleTextColor(reset) << std::endl;
|
||
|
|
std::cout << "Note: function performance varies depending on the proportion of edges" <<
|
||
|
|
std::endl << "that intersect with an imaginary horizontal line passing through the" <<
|
||
|
|
std::endl << "point of interest." << std::endl << std::endl;
|
||
|
|
|
||
|
|
unsigned int width = 600000, height = 400000, count = 10000000;
|
||
|
|
mp = Point64(width / 2, height / 2);
|
||
|
|
|
||
|
|
|
||
|
|
std::cout << std::endl << SetConsoleTextColor(yellow_bold) <<
|
||
|
|
"Benchmarks 1:" << SetConsoleTextColor(reset) << std::endl;
|
||
|
|
|
||
|
|
paths.clear();
|
||
|
|
for (int i = 0; i < 5; ++i)
|
||
|
|
paths.push_back(Ellipse(mp, width / 2.0, height / 2.0, count));
|
||
|
|
std::cout << "A single elliptical path (" <<
|
||
|
|
width << " x " << height << ")" << std::endl <<
|
||
|
|
"Edge count = " << count << ". " << std::endl <<
|
||
|
|
"Point (" << mp << ")" << std::endl << std::endl;
|
||
|
|
|
||
|
|
pipResults.clear();
|
||
|
|
pipResults.resize(3);
|
||
|
|
for (size_t i = 0; i < 3; ++i) pipResults[i].resize(paths.size());
|
||
|
|
|
||
|
|
benchmark::Initialize(0, nullptr);
|
||
|
|
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::CreateDefaultDisplayReporter());
|
||
|
|
|
||
|
|
std::cout << std::endl << std::endl << SetConsoleTextColor(yellow_bold) <<
|
||
|
|
"Benchmarks 2:" << SetConsoleTextColor(reset) << std::endl;
|
||
|
|
|
||
|
|
std::cout << "A random self-intersecting polygon (" <<
|
||
|
|
width << " x " << height << ")" << std::endl <<
|
||
|
|
"Edge count = " << count << ". " << std::endl <<
|
||
|
|
"Point (" << mp << ")" << std::endl << std::endl;
|
||
|
|
|
||
|
|
paths.clear();
|
||
|
|
for (int i = 0; i < 5; ++i)
|
||
|
|
paths.push_back(MakeRandomPoly(width, height, count));
|
||
|
|
|
||
|
|
pipResults.clear();
|
||
|
|
pipResults.resize(3);
|
||
|
|
for (size_t i = 0; i < 3; ++i) pipResults[i].resize(paths.size());
|
||
|
|
|
||
|
|
// rerun benchmarks using different polygons
|
||
|
|
benchmark::RunSpecifiedBenchmarks(benchmark::CreateDefaultDisplayReporter());
|
||
|
|
|
||
|
|
std::cout << std::endl;
|
||
|
|
// compare results to ensure they all agree :)
|
||
|
|
const std::string bad_filename = "test_pip_";
|
||
|
|
for (size_t i = 0; i < pipResults[0].size(); ++i)
|
||
|
|
{
|
||
|
|
if ((pipResults[0][i] == pipResults[1][i]) &&
|
||
|
|
(pipResults[0][i] == pipResults[2][i])) continue;
|
||
|
|
|
||
|
|
if (pipResults[0][i] != pipResults[1][i])
|
||
|
|
std::cout << "PIP2 returned the " << SetConsoleTextColor(red_bold) << "wrong " <<
|
||
|
|
SetConsoleTextColor(reset) << "result:" << std::endl;
|
||
|
|
if (pipResults[0][i] != pipResults[2][i])
|
||
|
|
std::cout << "PIP3 returned the " << SetConsoleTextColor(red_bold) << "wrong " <<
|
||
|
|
SetConsoleTextColor(reset) << "result:" << std::endl;
|
||
|
|
|
||
|
|
std::cout << "Problematic PIP path saved to - " << bad_filename << i << ".txt" << std::endl;
|
||
|
|
std::ofstream of(bad_filename);
|
||
|
|
of << paths[i] << std::endl;
|
||
|
|
of.close();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|