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);
}
extra_zone = extra_zone.GetClipped(Box({0, 0},
{static_cast<int32>(zone_image->Width()),
static_cast<int32>(zone_image->Height())}));
extra_zone.Clip(Box(
{0, 0},
{static_cast<int32>(zone_image->Width()), static_cast<int32>(zone_image->Height())}
));
for (const Zone &zone : zones) {
if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) {

View File

@ -22,47 +22,56 @@
#include "zm_line.h"
#include <cmath>
Polygon::Polygon(std::vector<Vector2> vertices) : vertices_(std::move(vertices)) {
int min_x = -1;
int max_x = -1;
int min_y = -1;
int max_y = -1;
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_) {
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_;
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});
calcArea();
calcCentre();
}
void Polygon::calcArea() {
double float_area = 0.0L;
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<int32>(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<int32>(std::lround(float_x)), static_cast<int32>(std::lround(float_y)));
}
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
Polygon Polygon::GetClipped(const Box &boundary) {
void Polygon::Clip(const Box &boundary) {
std::vector<Vector2> clipped_vertices = vertices_;
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 {
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<Vector2> vertices_;

View File

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