Polygon: Perform clip operation on existing object instead of returning a new clipped one

This commit is contained in:
Peter Keresztes Schmidt 2021-05-16 16:24:37 +02:00
parent 63cea992a0
commit b1de220958
5 changed files with 60 additions and 45 deletions

View File

@ -1481,9 +1481,10 @@ void Monitor::DumpZoneImage(const char *zone_string) {
zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
} }
extra_zone = extra_zone.GetClipped(Box({0, 0}, extra_zone.Clip(Box(
{static_cast<int32>(zone_image->Width()), {0, 0},
static_cast<int32>(zone_image->Height())})); {static_cast<int32>(zone_image->Width()), static_cast<int32>(zone_image->Height())}
));
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) { if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) {

View File

@ -22,47 +22,56 @@
#include "zm_line.h" #include "zm_line.h"
#include <cmath> #include <cmath>
Polygon::Polygon(std::vector<Vector2> vertices) : vertices_(std::move(vertices)) { Polygon::Polygon(std::vector<Vector2> vertices) : vertices_(std::move(vertices)), area(0) {
int min_x = -1; UpdateExtent();
int max_x = -1; UpdateArea();
int min_y = -1; UpdateCentre();
int max_y = -1; }
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_) { for (const Vector2 &vertex : vertices_) {
if (min_x == -1 || vertex.x_ < min_x) min_x = std::min(min_x, vertex.x_);
min_x = vertex.x_; max_x = std::max(max_x, vertex.x_);
if (max_x == -1 || vertex.x_ > max_x) min_y = std::min(min_y, vertex.y_);
max_x = vertex.x_; max_y = std::max(max_y, vertex.y_);
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}); extent = Box({min_x, min_y}, {max_x, max_y});
calcArea();
calcCentre();
} }
void Polygon::calcArea() { void Polygon::UpdateArea() {
double float_area = 0.0L; double float_area = 0.0;
for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { 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; float_area += trap_area;
} }
area = (int) round(fabs(float_area));
area = static_cast<int32>(std::lround(std::fabs(float_area)));
} }
void Polygon::calcCentre() { void Polygon::UpdateCentre() {
if (!area && !vertices_.empty()) if (!area && !vertices_.empty())
calcArea(); UpdateArea();
double float_x = 0.0L, float_y = 0.0L;
double float_x = 0.0;
double float_y = 0.0;
for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { 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_x += ((vertices_[i].y_ - vertices_[j].y_)
float_y += ((vertices_[j].x_ - vertices_[i].x_) * ((vertices_[i].y_ * 2) + (vertices_[i].y_ * vertices_[j].y_) + (vertices_[j].y_ * 2))); * ((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_x /= (6 * area);
float_y /= (6 * area); float_y /= (6 * area);
centre = Vector2((int) round(float_x), (int) round(float_y));
centre = Vector2(static_cast<int32>(std::lround(float_x)), static_cast<int32>(std::lround(float_y)));
} }
bool Polygon::Contains(const Vector2 &coord) const { bool Polygon::Contains(const Vector2 &coord) const {
@ -77,7 +86,7 @@ bool Polygon::Contains(const Vector2 &coord) const {
} }
// Clip the polygon to a rectangular boundary box using the Sutherland-Hodgman algorithm // 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<Vector2> clipped_vertices = vertices_; std::vector<Vector2> clipped_vertices = vertices_;
for (LineSegment const &clip_edge : boundary.Edges()) { for (LineSegment const &clip_edge : boundary.Edges()) {
@ -105,5 +114,8 @@ Polygon Polygon::GetClipped(const Box &boundary) {
} }
} }
return Polygon(clipped_vertices); vertices_ = clipped_vertices;
UpdateExtent();
UpdateArea();
UpdateCentre();
} }

View File

@ -40,6 +40,7 @@ struct Edge {
} }
}; };
// This class represents convex or concave non-self-intersecting polygons.
class Polygon { class Polygon {
public: public:
Polygon() : area(0) {} Polygon() : area(0) {}
@ -55,11 +56,12 @@ class Polygon {
bool Contains(const Vector2 &coord) const; bool Contains(const Vector2 &coord) const;
Polygon GetClipped(const Box &boundary); void Clip(const Box &boundary);
private: private:
void calcArea(); void UpdateExtent();
void calcCentre(); void UpdateArea();
void UpdateCentre();
private: private:
std::vector<Vector2> vertices_; std::vector<Vector2> vertices_;

View File

@ -885,7 +885,7 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
polygon.Extent().Hi().x_, polygon.Extent().Hi().x_,
polygon.Extent().Hi().y_); polygon.Extent().Hi().y_);
polygon = polygon.GetClipped(Box( polygon.Clip(Box(
{0, 0}, {0, 0},
{static_cast<int32>(monitor->Width()), static_cast<int32>(monitor->Height())} {static_cast<int32>(monitor->Width()), static_cast<int32>(monitor->Height())}
)); ));

View File

@ -56,16 +56,16 @@ TEST_CASE("Polygon: clipping") {
REQUIRE(p.Extent().Size() == Vector2(8, 7)); REQUIRE(p.Extent().Size() == Vector2(8, 7));
SECTION("boundary box larger than polygon") { 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(p.GetVertices().size() == 11);
REQUIRE(c.Extent().Size() == Vector2(8, 7)); REQUIRE(p.Extent().Size() == Vector2(8, 7));
} }
SECTION("boundary box smaller than polygon") { 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(p.GetVertices().size() == 8);
REQUIRE(c.Extent().Size() == Vector2(8, 3)); REQUIRE(p.Extent().Size() == Vector2(8, 3));
} }
} }