122 lines
4.0 KiB
C++
122 lines
4.0 KiB
C++
//
|
|
// ZoneMinder Polygon Class Implementation, $Date$, $Revision$
|
|
// Copyright (C) 2001-2008 Philip Coombes
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
|
|
#include "zm_poly.h"
|
|
|
|
#include "zm_line.h"
|
|
#include <cmath>
|
|
|
|
Polygon::Polygon(std::vector<Vector2> vertices) : vertices_(std::move(vertices)), area(0) {
|
|
UpdateExtent();
|
|
UpdateArea();
|
|
UpdateCentre();
|
|
}
|
|
|
|
void Polygon::UpdateExtent() {
|
|
if (vertices_.empty())
|
|
return;
|
|
|
|
int min_x = vertices_[0].x_;
|
|
int max_x = 0;
|
|
int min_y = vertices_[0].y_;
|
|
int max_y = 0;
|
|
for (const Vector2 &vertex : vertices_) {
|
|
min_x = std::min(min_x, vertex.x_);
|
|
max_x = std::max(max_x, vertex.x_);
|
|
min_y = std::min(min_y, vertex.y_);
|
|
max_y = std::max(max_y, vertex.y_);
|
|
}
|
|
|
|
extent = Box({min_x, min_y}, {max_x, max_y});
|
|
}
|
|
|
|
void Polygon::UpdateArea() {
|
|
double float_area = 0.0;
|
|
for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) {
|
|
double trap_area = ((vertices_[i].x_ - vertices_[j].x_) * ((vertices_[i].y_ + vertices_[j].y_))) / 2.0;
|
|
float_area += trap_area;
|
|
}
|
|
|
|
area = static_cast<int32>(std::lround(std::fabs(float_area)));
|
|
}
|
|
|
|
void Polygon::UpdateCentre() {
|
|
if (!area && !vertices_.empty())
|
|
UpdateArea();
|
|
|
|
double float_x = 0.0;
|
|
double float_y = 0.0;
|
|
for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) {
|
|
float_x += ((vertices_[i].y_ - vertices_[j].y_)
|
|
* ((vertices_[i].x_ * 2) + (vertices_[i].x_ * vertices_[j].x_) + (vertices_[j].x_ * 2)));
|
|
float_y += ((vertices_[j].x_ - vertices_[i].x_)
|
|
* ((vertices_[i].y_ * 2) + (vertices_[i].y_ * vertices_[j].y_) + (vertices_[j].y_ * 2)));
|
|
}
|
|
float_x /= (6 * area);
|
|
float_y /= (6 * area);
|
|
|
|
centre = Vector2(static_cast<int32>(std::lround(float_x)), static_cast<int32>(std::lround(float_y)));
|
|
}
|
|
|
|
bool Polygon::Contains(const Vector2 &coord) const {
|
|
bool inside = false;
|
|
for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) {
|
|
if ((((vertices_[i].y_ <= coord.y_) && (coord.y_ < vertices_[j].y_)) || ((vertices_[j].y_ <= coord.y_) && (coord.y_ < vertices_[i].y_)))
|
|
&& (coord.x_ < (vertices_[j].x_ - vertices_[i].x_) * (coord.y_ - vertices_[i].y_) / (vertices_[j].y_ - vertices_[i].y_) + vertices_[i].x_)) {
|
|
inside = !inside;
|
|
}
|
|
}
|
|
return inside;
|
|
}
|
|
|
|
// Clip the polygon to a rectangular boundary box using the Sutherland-Hodgman algorithm
|
|
void Polygon::Clip(const Box &boundary) {
|
|
std::vector<Vector2> clipped_vertices = vertices_;
|
|
|
|
for (LineSegment const &clip_edge : boundary.Edges()) {
|
|
// convert our line segment to an infinite line
|
|
Line clip_line = Line(clip_edge);
|
|
|
|
std::vector<Vector2> to_clip = clipped_vertices;
|
|
clipped_vertices.clear();
|
|
|
|
for (size_t i = 0; i < to_clip.size(); ++i) {
|
|
Vector2 vert1 = to_clip[i];
|
|
Vector2 vert2 = to_clip[(i + 1) % to_clip.size()];
|
|
|
|
bool vert1_left = clip_line.IsPointLeftOfOrColinear(vert1);
|
|
bool vert2_left = clip_line.IsPointLeftOfOrColinear(vert2);
|
|
|
|
if (vert2_left) {
|
|
if (!vert1_left) {
|
|
clipped_vertices.push_back(Line(vert1, vert2).Intersection(clip_line));
|
|
}
|
|
clipped_vertices.push_back(vert2);
|
|
} else if (vert1_left) {
|
|
clipped_vertices.push_back(Line(vert1, vert2).Intersection(clip_line));
|
|
}
|
|
}
|
|
}
|
|
|
|
vertices_ = clipped_vertices;
|
|
UpdateExtent();
|
|
UpdateArea();
|
|
UpdateCentre();
|
|
}
|