// // 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 Polygon::Polygon(std::vector 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(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(std::lround(float_x)), static_cast(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 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 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(); }