diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 36d04bbec..179c32b54 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1481,9 +1481,10 @@ void Monitor::DumpZoneImage(const char *zone_string) { zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); } - extra_zone = extra_zone.GetClipped(Box({0, 0}, - {static_cast(zone_image->Width()), - static_cast(zone_image->Height())})); + extra_zone.Clip(Box( + {0, 0}, + {static_cast(zone_image->Width()), static_cast(zone_image->Height())} + )); for (const Zone &zone : zones) { if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) { diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index 0bef5a4fa..6485bc192 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -22,47 +22,56 @@ #include "zm_line.h" #include -Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)) { - int min_x = -1; - int max_x = -1; - int min_y = -1; - int max_y = -1; - for (const Vector2 &vertex : vertices_) { - if (min_x == -1 || vertex.x_ < min_x) - min_x = vertex.x_; - if (max_x == -1 || vertex.x_ > max_x) - max_x = vertex.x_; - if (min_y == -1 || vertex.y_ < min_y) - min_y = vertex.y_; - if (max_y == -1 || vertex.y_ > max_y) - max_y = vertex.y_; - } - extent = Box({min_x, min_y}, {max_x, max_y}); - - calcArea(); - calcCentre(); +Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)), area(0) { + UpdateExtent(); + UpdateArea(); + UpdateCentre(); } -void Polygon::calcArea() { - double float_area = 0.0L; +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.0L; + double trap_area = ((vertices_[i].x_ - vertices_[j].x_) * ((vertices_[i].y_ + vertices_[j].y_))) / 2.0; float_area += trap_area; } - area = (int) round(fabs(float_area)); + + area = static_cast(std::lround(std::fabs(float_area))); } -void Polygon::calcCentre() { +void Polygon::UpdateCentre() { if (!area && !vertices_.empty()) - calcArea(); - double float_x = 0.0L, float_y = 0.0L; + 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 += ((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((int) round(float_x), (int) round(float_y)); + + centre = Vector2(static_cast(std::lround(float_x)), static_cast(std::lround(float_y))); } bool Polygon::Contains(const Vector2 &coord) const { @@ -77,10 +86,10 @@ bool Polygon::Contains(const Vector2 &coord) const { } // Clip the polygon to a rectangular boundary box using the Sutherland-Hodgman algorithm -Polygon Polygon::GetClipped(const Box &boundary) { +void Polygon::Clip(const Box &boundary) { std::vector clipped_vertices = vertices_; - for (LineSegment const& clip_edge : boundary.Edges()) { + for (LineSegment const &clip_edge : boundary.Edges()) { // convert our line segment to an infinite line Line clip_line = Line(clip_edge); @@ -105,5 +114,8 @@ Polygon Polygon::GetClipped(const Box &boundary) { } } - return Polygon(clipped_vertices); + vertices_ = clipped_vertices; + UpdateExtent(); + UpdateArea(); + UpdateCentre(); } diff --git a/src/zm_poly.h b/src/zm_poly.h index 63b85b8da..0d1981ef2 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -40,6 +40,7 @@ struct Edge { } }; +// This class represents convex or concave non-self-intersecting polygons. class Polygon { public: Polygon() : area(0) {} @@ -55,11 +56,12 @@ class Polygon { bool Contains(const Vector2 &coord) const; - Polygon GetClipped(const Box &boundary); + void Clip(const Box &boundary); private: - void calcArea(); - void calcCentre(); + void UpdateExtent(); + void UpdateArea(); + void UpdateCentre(); private: std::vector vertices_; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 7a73b93ed..41174857f 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -885,7 +885,7 @@ std::vector Zone::Load(Monitor *monitor) { polygon.Extent().Hi().x_, polygon.Extent().Hi().y_); - polygon = polygon.GetClipped(Box( + polygon.Clip(Box( {0, 0}, {static_cast(monitor->Width()), static_cast(monitor->Height())} )); diff --git a/tests/zm_poly.cpp b/tests/zm_poly.cpp index 8ce775d6c..c7e37673b 100644 --- a/tests/zm_poly.cpp +++ b/tests/zm_poly.cpp @@ -56,16 +56,16 @@ TEST_CASE("Polygon: clipping") { REQUIRE(p.Extent().Size() == Vector2(8, 7)); SECTION("boundary box larger than polygon") { - Polygon c = p.GetClipped(Box({1, 0}, {11, 9})); + p.Clip(Box({1, 0}, {11, 9})); - REQUIRE(c.GetVertices().size() == 11); - REQUIRE(c.Extent().Size() == Vector2(8, 7)); + REQUIRE(p.GetVertices().size() == 11); + REQUIRE(p.Extent().Size() == Vector2(8, 7)); } SECTION("boundary box smaller than polygon") { - Polygon c = p.GetClipped(Box({2, 4}, {10, 7})); + p.Clip(Box({2, 4}, {10, 7})); - REQUIRE(c.GetVertices().size() == 8); - REQUIRE(c.Extent().Size() == Vector2(8, 3)); + REQUIRE(p.GetVertices().size() == 8); + REQUIRE(p.Extent().Size() == Vector2(8, 3)); } }