You could really do with some more types
struct Point
{
double x;
double y;
double z; // ???
};
struct BasicPolygon
{
std::vector<Point> shape;
};
struct Polygon
{
BasicPolygon shell;
std::vector<BasicPolygon> holes;
};
Start by =default
ing operator<=>
for these types, and checking that cases we want to be equal are treated as equal.
At this point a polygon is equal to another if every point matches, in the same order. If you require that polygons differing only in the start vertex are treated as equal, things will be harder. If that's the case, I'd actually recommend a step where you canonicalize all your polygons to start at the lowest point.
void BasicPolygon::canonicalize()
{
auto it = std::min_element(shape.begin(), shape.end());
std::rotate(shape.begin(), it, shape.end());
}
void Polygon::canonicalize()
{
shell.canonicalize();
std::for_each(holes.begin(), holes.end(), std::mem_fn(&BasicPolygon::canonicalize));
}
If you can't mutate the geometry, you can find the canonical start point each time you compare, and do a sequence of std::lexicographic_compare
s. N.b. this is a different total order than canonicalizing first, because of the size check.
bool operator<(const BasicPolygon & lhs, const BasicPolygon & rhs)
{
if (lhs.size() < rhs.size()) return true;
if (rhs.size() < lhs.size()) return false;
auto l_first = std::min_element(lhs.shape.begin(), lhs.shape.end());
auto r_first = std::min_element(rhs.shape.begin(), rhs.shape.end());
const auto l_last = l_first;
const auto r_last = r_first;
auto l_rem = std::distance(l_first, lhs.shape.end());
auto r_rem = std::distance(r_first, rhs.shape.end());
if (l_rem < r_rem)
{
if (std::lexicographic_compare(l_first, lhs.shape.end(), r_first, r_first + l_rem)) return true;
if (std::lexicographic_compare(r_first, r_first + l_rem, l_first, lhs.shape.end()) return false;
r_first += l_rem;
r_rem -= l_rem;
l_first = lhs.shape.begin();
if (std::lexicographic_compare(l_first, l_first + r_rem, r_first, rhs.shape.end()) return true;
if (std::lexicographic_compare(r_first, rhs.shape.end(), l_first, l_first + r_rem)) return false;
l_first += r_rem;
r_first = rhs.shape.begin();
if (std::lexicographic_compare(l_first, l_last, r_first, r_last)) return true;
if (std::lexicographic_compare(r_first, r_last, l_first, l_last) return false;
} else {
if (std::lexicographic_compare(l_first, l_first + r_rem, r_first, rhs.shape.end()) return true;
if (std::lexicographic_compare(r_first, rhs.shape.end(), l_first, l_first + r_rem)) return false;
l_first += r_rem;
l_rem -= r_rem;
r_first = rhs.shape.begin();
if (std::lexicographic_compare(l_first, lhs.shape.end(), r_first, r_first + l_rem)) return true;
if (std::lexicographic_compare(r_first, r_first + l_rem, l_first, lhs.shape.end()) return false;
r_first += l_rem;
l_first = lhs.shape.begin();
if (std::lexicographic_compare(l_first, l_last, r_first, r_last)) return true;
if (std::lexicographic_compare(r_first, r_last, l_first, l_last) return false;
}
return false;
}