定义Polygon类,作为多边形,这是一个常用的几何对象。Polygon是有相同首尾点的Polyline,因此我选择在Polyline的尾部添加一个和头部点相同的点作为尾部点,而非像某些实现那样在获取第尾部点时返回头部点。
下面给出Polygon的声明。
class Polygon : public Polyline
{
public:
Polygon() {};
Polygon(const Polygon &polygon);
Polygon(std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end);
Polygon(const std::initializer_list<Point> &points);
Polygon(const Polyline &polyline);
Polygon(const AABBRect &rect);
Polygon &operator=(const Polygon &polygon);
const Type type() const override;
Polygon *clone() const override;
void reorder_points(const bool cw = true);
// 判断点顺序是否为顺时针
bool is_cw() const;
void append(const Point &point) override;
void append(const Polyline &polyline) override;
void append(std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end) override;
void insert(const size_t index, const Point &point) override;
void insert(const size_t index, const Polyline &polyline) override;
void insert(const size_t index, std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end) override;
void remove(const size_t index) override;
void remove(const size_t index, const size_t count) override;
Point pop(const size_t index) override;
Polygon operator+(const Point &point) const;
Polygon operator-(const Point &point) const;
void operator+=(const Point &point);
void operator-=(const Point &point);
const double area() const;
size_t next_point_index(const size_t index) const;
const Point &next_point(const size_t index) const;
Point &next_point(const size_t index);
size_t last_point_index(const size_t index) const;
const Point &last_point(const size_t index) const;
Point &last_point(const size_t index);
size_t index(const double x, const double y) const;
size_t index(const Point &point) const;
};
下面给出Polygon的实现。
Polygon::Polygon(const Polygon &polygon)
:Polyline(polygon)
{}
Polygon::Polygon(std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end)
:Polyline(begin, end)
{
assert(size() >= 3);
if (_points.back() != _points.front())
{
_points.emplace_back(_points.front());
}
}
Polygon::Polygon(const std::initializer_list<Point> &points)
:Polyline(points)
{
assert(size() > 2);
if (_points.back() != _points.front())
{
_points.emplace_back(_points.front());
}
}
Polygon::Polygon(const Polyline &polyline)
:Polyline(polyline)
{
if (_points.back() != _points.front())
{
_points.emplace_back(_points.front());
}
}
Polygon::Polygon(const AABBRect& rect)
:Polyline(rect.cbegin(), rect.cend())
{}
Polygon &Polygon::operator=(const Polygon &polygon)
{
if (this != &polygon)
{
Polyline::operator=(polygon);
}
return *this;
}
const Type Polygon::type() const
{
return Type::POLYGON;
}
Polygon *Polygon::clone() const
{
return new Polygon(*this);
}
void Polygon::reorder_points(const bool cw)
{
if (size() < 4)
{
return;
}
double result = 0;
for (size_t i = 0, count = size() - 1; i < count; ++i)
{
result += (_points[i].x * _points[i + 1].y - _points[i + 1].x * _points[i].y);
}
if (cw)
{
if (result > 0)
{
std::reverse(_points.begin(), _points.end());
}
}
else
{
if (result < 0)
{
std::reverse(_points.begin(), _points.end());
}
}
}
bool Polygon::is_cw() const
{
if (size() < 4)
{
return false;
}
double result = 0;
for (size_t i = 0, count = size() - 1; i < count; ++i)
{
result += (_points[i].x * _points[i + 1].y - _points[i + 1].x * _points[i].y);
}
return result < 0;
}
void Polygon::append(const Point &point)
{
if (size() < 2)
{
Polyline::append(point);
}
else
{
if (_points.front() == _points.back())
{
Polyline::insert(size() - 1, point);
}
else
{
_points.emplace_back(point);
_points.emplace_back(_points.front());
}
}
}
void Polygon::append(const Polyline &polyline)
{
if (empty())
{
Polyline::append(polyline);
if (_points.front() != _points.back())
{
_points.emplace_back(_points.front());
}
}
else
{
if (_points.front() == _points.back())
{
Polyline::insert(size() - 1, polyline);
}
else
{
Polyline::append(polyline);
_points.emplace_back(_points.front());
}
}
}
void Polygon::append(std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end)
{
if (empty())
{
Polyline::append(begin, end);
if (_points.front() != _points.back())
{
_points.emplace_back(_points.front());
}
}
else
{
if (_points.front() == _points.back())
{
Polyline::insert(size() - 1, begin, end);
}
else
{
_points.insert(_points.end(), begin, end);
_points.emplace_back(_points.front());
}
}
}
void Polygon::insert(const size_t index, const Point &point)
{
Polyline::insert(index, point);
if (index == 0)
{
_points.back() = _points.front();
}
}
void Polygon::insert(const size_t index, const Polyline &polyline)
{
Polyline::insert(index, polyline);
if (index == 0)
{
_points.back() = _points.front();
}
}
void Polygon::insert(const size_t index, std::vector<Point>::const_iterator begin, std::vector<Point>::const_iterator end)
{
Polyline::insert(index, begin, end);
if (index == 0)
{
_points.back() = _points.front();
}
}
void Polygon::remove(const size_t index)
{
Polyline::remove(index);
if (index == 0)
{
_points.back() = _points.front();
}
else if (index == size())
{
_points.front() == _points.back();
}
}
void Polygon::remove(const size_t index, const size_t count)
{
Polyline::remove(index, count);
if (size() > 2)
{
if (index == 0)
{
back() = front();
}
else if (index + count >= size())
{
front() = back();
}
}
}
Point Polygon::pop(const size_t index)
{
Geo::Point point = Polyline::pop(index);
if (index == 0)
{
_points.back() = _points.front();
}
else if (index == size())
{
_points.front() = _points.back();
}
return point;
}
Polygon Polygon::operator+(const Point &point) const
{
std::vector<Point> temp(begin(), end());
for (Point &p : temp)
{
p += point;
}
return Polygon(temp.begin(), temp.end());
}
Polygon Polygon::operator-(const Point &point) const
{
std::vector<Point> temp(begin(), end());
for (Point &p : temp)
{
p -= point;
}
return Polygon(temp.begin(), temp.end());
}
void Polygon::operator+=(const Point &point)
{
for (Point &p : _points)
{
p += point;
}
}
void Polygon::operator-=(const Point &point)
{
for (Point &p : _points)
{
p -= point;
}
}
const double Polygon::area() const
{
if (size() < 4)
{
return 0;
}
double result = 0;
for (size_t i = 0, count = size() - 1; i < count; ++i)
{
result += (_points[i].x * _points[i + 1].y - _points[i + 1].x * _points[i].y);
}
return std::abs(result) / 2.0;
}
size_t Polygon::next_point_index(const size_t index) const
{
if (index < size() - 1)
{
return index + 1;
}
else
{
return 1;
}
}
const Point &Polygon::next_point(const size_t index) const
{
if (index < size() - 1)
{
return _points[index + 1];
}
else
{
return _points[1];
}
}
Point &Polygon::next_point(const size_t index)
{
if (index < size() - 1)
{
return _points[index + 1];
}
else
{
return _points[1];
}
}
size_t Polygon::last_point_index(const size_t index) const
{
if (index > 0)
{
return index - 1;
}
else
{
return size() - 2;
}
}
const Point &Polygon::last_point(const size_t index) const
{
if (index > 0)
{
return _points[index - 1];
}
else
{
return _points[_points.size() - 2];
}
}
Point &Polygon::last_point(const size_t index)
{
if (index > 0)
{
return _points[index - 1];
}
else
{
return _points[_points.size() - 2];
}
}
size_t Polygon::index(const double x, const double y) const
{
for (size_t i = 0, count = _points.size() - 1; i < count; ++i)
{
if (_points[i].x == x && _points[i].y == y)
{
return i;
}
}
return SIZE_MAX;
}
size_t Polygon::index(const Point &point) const
{
for (size_t i = 0, count = _points.size() - 1; i < count; ++i)
{
if (_points[i] == point)
{
return i;
}
}
return SIZE_MAX;
}