Skip to content

Commit

Permalink
Implement: In/Out polygon vertex
Browse files Browse the repository at this point in the history
  • Loading branch information
ntut-xuan committed Dec 27, 2024
1 parent 29238e4 commit b16e5c6
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 73 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20)
project(PolygonDust)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -O3 -pedantic")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -O3 -pedantic -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-Werror -O3 -g")

include(FetchContent)
Expand Down
3 changes: 2 additions & 1 deletion includes/line.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class Line {
return std::optional<Point>();
}
if (this->start.GetX() == this->end.GetX()) {
if (Between<double>(y, this->start.GetY(), this->end.GetY())) {
if (Between<double>(y, std::min(this->start.GetY(), this->end.GetY()),
std::max(this->start.GetY(), this->end.GetY()))) {
return std::optional<Point>({this->start.GetX(), y});
} else {
return std::optional<Point>();
Expand Down
3 changes: 2 additions & 1 deletion includes/polygon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Polygon {
double GetMaxY() { return max_y; }
bool IsLocalMinMaxPoint(Point point);
bool IsClockwise() { return clockwise; }
bool IsPointInPolygon(Point point);
bool IsVertexInPolygon(Point point);
};

#endif
110 changes: 59 additions & 51 deletions includes/polygon_clipping.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef POLYGON_CLIPPING_HPP
#define POLYGON_CLIPPING_HPP

#include "line.hpp"
#include "point.hpp"
#include "polygon.hpp"
#include "shared.hpp"
Expand All @@ -12,12 +13,33 @@
#include <optional>
#include <vector>

class PointWithState {
private:
Point point;
bool in_polygon;

public:
PointWithState(Point point, bool in_polygon) {
this->point = point;
this->in_polygon = in_polygon;
}
Point GetPoint() const { return this->point; }
bool IsInPolygon() const { return this->in_polygon; }
bool operator==(const PointWithState &other) const {
return this->point == other.point && this->in_polygon == other.in_polygon;
}
friend void PrintTo(const PointWithState &point_with_state, std::ostream *os) {
*os << point_with_state.ToString();
}
std::string ToString() const { return point.ToString() + " - " + (in_polygon ? "InPolygon" : "OutPolygon"); }
};

class PolygonClipping {
private:
std::shared_ptr<Polygon> subjectPolygon;
std::shared_ptr<Polygon> clippingPolygon;
std::vector<Point> subject_list;
std::vector<Point> clipping_list;
std::vector<PointWithState> subject_list;
std::vector<PointWithState> clipping_list;
std::map<Point, int> point_to_intersection_index;

std::optional<int> GetIntersectionPointIndex(Point point) {
Expand All @@ -31,11 +53,11 @@ class PolygonClipping {

int FindIntersectIndex(std::optional<int> subject_intersect_index) {
int result = 0;
std::optional<int> clipping_intersect_index = GetIntersectionPointIndex(clipping_list[result]);
std::optional<int> clipping_intersect_index = GetIntersectionPointIndex(clipping_list[result].GetPoint());
while (!(clipping_intersect_index.has_value() &&
subject_intersect_index.value() == clipping_intersect_index.value())) {
result += 1;
clipping_intersect_index = GetIntersectionPointIndex(clipping_list[result]);
clipping_intersect_index = GetIntersectionPointIndex(clipping_list[result].GetPoint());
}
return result;
}
Expand All @@ -46,20 +68,18 @@ class PolygonClipping {
size_t clipping_list_size = clipping_list.size();
size_t query_node_index = (subject_list_index) % (subject_list.size());

std::optional<int> subject_intersect_index = GetIntersectionPointIndex(subject_list[query_node_index]);
std::optional<int> subject_intersect_index =
GetIntersectionPointIndex(subject_list[query_node_index].GetPoint());

int clip_list_intersect_start_index = FindIntersectIndex(subject_intersect_index);

for (size_t j = 0; j < clipping_list_size; j++) {
int query_index = (clip_list_intersect_start_index + j + 1) % clipping_list_size;
clip_vertexes.push_back(clipping_list[query_index]);

std::cout << "Rounding clipping_list node " << clipping_list[query_index].ToString() << std::endl;
clip_vertexes.push_back(clipping_list[query_index].GetPoint());

std::optional<int> intersect_index = GetIntersectionPointIndex(clipping_list[query_index]);
std::optional<int> intersect_index = GetIntersectionPointIndex(clipping_list[query_index].GetPoint());

if (intersect_index.has_value()) {
std::cout << "Found next intersect point." << std::endl;
clip_list_intersect_end_id = intersect_index.value();
break;
}
Expand All @@ -69,10 +89,9 @@ class PolygonClipping {
subject_list_index += 1;
subject_list_index %= subject_list_size;
int query_index = (subject_list_index) % subject_list_size;
std::optional<int> intersect_index = GetIntersectionPointIndex(subject_list[query_index]);
std::optional<int> intersect_index = GetIntersectionPointIndex(subject_list[query_index].GetPoint());

if (intersect_index.has_value() && intersect_index.value() == clip_list_intersect_end_id) {
std::cout << "Move to next subject list node." << std::endl;
break;
}
}
Expand All @@ -83,62 +102,35 @@ class PolygonClipping {
: subjectPolygon(subjectPolygon), clippingPolygon(clippingPolygon) {}

Polygon Produce() {

std::cout << "Process Create Vertex List." << std::endl;

CreateVertexList(subject_list, subjectPolygon, clippingPolygon);
CreateVertexList(clipping_list, clippingPolygon, subjectPolygon);

for (Point point : subject_list) {
if (GetIntersectionPointIndex(point).has_value()) {
std::cout << "Subject Polygon have intersect point " << point.ToString() << std::endl;
} else {
std::cout << "Subject Polygon have point " << point.ToString() << std::endl;
}
}

for (Point point : clipping_list) {
if (GetIntersectionPointIndex(point).has_value()) {
std::cout << "Clipping Polygon have intersect point " << point.ToString() << std::endl;
} else {
std::cout << "Clipping Polygon have point " << point.ToString() << std::endl;
}
}

// subjectPolygon clockwise should different on clippingPolygon.
if ((subjectPolygon->IsClockwise() ^ clippingPolygon->IsClockwise()) == 0) {
std::reverse(clipping_list.begin(), clipping_list.end());
}

std::cout << "Process Polygon." << std::endl;

std::vector<Point> clip_vertexes;

std::cout << "Finding Start Node." << std::endl;

int subject_start_node = 0;

for (size_t i = 0; i < subject_list.size(); i++) {
Point point = subject_list[i];
Point point = subject_list[i].GetPoint();
if (!(Between<double>(point.GetX(), clippingPolygon->GetMinX(), clippingPolygon->GetMaxX()) &&
Between<double>(point.GetY(), clippingPolygon->GetMinY(), clippingPolygon->GetMaxY()))) {
subject_start_node = i;
break;
}
}

std::cout << "Found start node, start rounding." << std::endl;

for (size_t i = subject_start_node, j = 0; j < subject_list.size(); i++, j++) {
size_t query_node_index = (i) % (subject_list.size());
clip_vertexes.push_back(subject_list[query_node_index]);
clip_vertexes.push_back(subject_list[query_node_index].GetPoint());

std::cout << "Rounding subject node " << subject_list[query_node_index].ToString() << std::endl;

std::optional<int> subject_intersect_index = GetIntersectionPointIndex(subject_list[query_node_index]);
std::optional<int> subject_intersect_index =
GetIntersectionPointIndex(subject_list[query_node_index].GetPoint());

if (subject_intersect_index.has_value()) {
std::cout << "Found intersect point, switch list into clip_vertexes." << std::endl;
ProcessIntersectPoint(clip_vertexes, i);
}
}
Expand All @@ -155,9 +147,10 @@ class PolygonClipping {
return Polygon(std::vector<Point>(clip_vertexes.begin(), clip_vertexes.begin() + duplicate_index));
}

void CreateVertexList(std::vector<Point> &list, std::shared_ptr<Polygon> major_polygon,
void CreateVertexList(std::vector<PointWithState> &list, std::shared_ptr<Polygon> major_polygon,
std::shared_ptr<Polygon> minor_polygon) {
list.push_back(major_polygon->GetVertexs()->at(0));
Point first_major_point = major_polygon->GetVertexs()->at(0);
list.push_back(PointWithState(first_major_point, major_polygon->IsPointInPolygon(first_major_point)));

size_t major_polygon_vertex_size = major_polygon->GetVertexs()->size();
size_t minor_polygon_vertex_size = minor_polygon->GetVertexs()->size();
Expand All @@ -166,34 +159,49 @@ class PolygonClipping {
Point v12 = major_polygon->GetVertexs()->at((i + 1) % major_polygon_vertex_size);
Line line1(v11, v12);

std::vector<Point> intersect_points;
std::vector<PointWithState> intersect_points;
std::vector<double> x_set;

for (size_t j = 0; j < minor_polygon_vertex_size; j++) {
Point v21 = minor_polygon->GetVertexs()->at((j) % minor_polygon_vertex_size);
Point v22 = minor_polygon->GetVertexs()->at((j + 1) % minor_polygon_vertex_size);
Line line2(v21, v22);

std::optional<Point> intersect_y_ray_optional = line2.GetYRayIntersectPoint(v12.GetY());

if (intersect_y_ray_optional.has_value()) {
if (std::find(x_set.begin(), x_set.end(), intersect_y_ray_optional->GetX()) == x_set.end()) {
x_set.push_back(intersect_y_ray_optional->GetX());
}
if ((std::find(x_set.begin(), x_set.end(), intersect_y_ray_optional->GetX()) != x_set.end()) &&
minor_polygon->IsLocalMinMaxPoint(intersect_y_ray_optional.value())) {
x_set.push_back(intersect_y_ray_optional->GetX());
}
}

std::optional<Point> intersect_point_optional = line1.GetIntersectPoint(line2);

if (intersect_point_optional.has_value()) {
std::cout << "Create Intersect Point " << intersect_point_optional.value().ToString() << std::endl;
Point intersect_point = intersect_point_optional.value();
intersect_points.push_back(intersect_point);
intersect_points.push_back(PointWithState(intersect_point, true));
if (point_to_intersection_index.find(intersect_point) == point_to_intersection_index.end()) {
point_to_intersection_index[intersect_point] = point_to_intersection_index.size();
}
}
}

std::sort(intersect_points.begin(), intersect_points.end(),
[v11](const Point &a, const Point &b) { return GetDistance(v11, a) < GetDistance(v11, b); });
[v11](const PointWithState &a, const PointWithState &b) {
return GetDistance(v11, a.GetPoint()) < GetDistance(v11, b.GetPoint());
});

for (Point p : intersect_points) {
for (PointWithState p : intersect_points) {
list.push_back(p);
}

if (i != major_polygon_vertex_size - 1) {
list.push_back(v12);
bool in_polygon = DeterminePointInPolygonByXSet(x_set, v12.GetX());
list.push_back(PointWithState(v12, in_polygon));
}
}
}
Expand Down
24 changes: 16 additions & 8 deletions includes/shared.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,32 @@

#include "point.hpp"
#include <cmath>
#include <set>
#include <vector>

inline double GetMatrixComputationValue(double a, double b, double c, double d){
return a * d - b * c;
}
inline double GetMatrixComputationValue(double a, double b, double c, double d) { return a * d - b * c; }

inline static double GetDistance(Point first, Point second){
inline static double GetDistance(Point first, Point second) {
double dx = (first.GetX() - second.GetX());
double dy = (first.GetY() - second.GetY());
return std::sqrt(dx * dx + dy * dy);
}

template<class T>
inline static bool Between(T val, T min, T max){
if(min <= val && val <= max){
template <class T> inline static bool Between(T val, T min, T max) {
if (min <= val && val <= max) {
return true;
}
return false;
}

inline static bool DeterminePointInPolygonByXSet(std::vector<double> x_set, double x) {
int less_than_x = 0;
for (size_t i = 0; i < x_set.size(); i++) {
if (x_set[i] < x) {
less_than_x += 1;
}
}
int larger_than_x = x_set.size() - less_than_x;
return larger_than_x % 2 == 1 && less_than_x % 2 == 1;
}

#endif
33 changes: 32 additions & 1 deletion src/polygon.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "polygon.hpp"
#include "point.hpp"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <memory>
Expand Down Expand Up @@ -27,4 +28,34 @@ std::shared_ptr<std::vector<Point>> Polygon::GetLocalMinMaxVertexs() {
return std::make_shared<std::vector<Point>>(localminmaxs->begin(), localminmaxs->end());
}

bool Polygon::IsLocalMinMaxPoint(Point point) { return localminmaxs->find(point) != localminmaxs->end(); }
bool Polygon::IsLocalMinMaxPoint(Point point) { return localminmaxs->find(point) != localminmaxs->end(); }

bool Polygon::IsPointInPolygon(Point point) {
std::vector<double> x_set;
double x = point.GetX();
double y = point.GetY();
for (size_t i = 0; i < vertexs->size(); i++) {
Point start = vertexs->at(i);
Point end = vertexs->at((i + 1) % vertexs->size());
Line line(start, end);

std::optional<Point> intersect_point = line.GetYRayIntersectPoint(y);

if (!intersect_point.has_value()) {
continue;
}

if (std::find(x_set.begin(), x_set.end(), intersect_point->GetX()) == x_set.end()) {
x_set.push_back(intersect_point->GetX());
}
if ((std::find(x_set.begin(), x_set.end(), intersect_point->GetX()) != x_set.end()) &&
this->IsLocalMinMaxPoint(intersect_point.value())) {
x_set.push_back(intersect_point->GetX());
}
}
return DeterminePointInPolygonByXSet(x_set, x);
}

bool Polygon::IsVertexInPolygon(Point point) {
return std::find(vertexs->begin(), vertexs->end(), point) != vertexs->end();
}
22 changes: 15 additions & 7 deletions tests/cpp/ut_polygon_clipping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ TEST(POLYGON_CLIPPING_TEST, test_create_vertex_list_should_return_correct_vertex
std::shared_ptr<Polygon> polygon_ptr1 = std::make_shared<Polygon>(polygon1);
std::shared_ptr<Polygon> polygon_ptr2 = std::make_shared<Polygon>(polygon2);
PolygonClipping polygon_clipping(polygon_ptr1, polygon_ptr2);
std::vector<Point> points;
std::vector<PointWithState> points;

polygon_clipping.CreateVertexList(points, polygon_ptr1, polygon_ptr2);

ASSERT_EQ(points, std::vector<Point>({Point(1, 5), Point(3, 2), Point(6, 6), Point(8, 5), Point(10, 4),
Point(11.5, 10), Point(12, 12), Point(6, 12)}));
ASSERT_EQ(points,
std::vector<PointWithState>({PointWithState(Point(1, 5), false), PointWithState(Point(3, 2), false),
PointWithState(Point(6, 6), false), PointWithState(Point(8, 5), true),
PointWithState(Point(10, 4), true), PointWithState(Point(11.5, 10), true),
PointWithState(Point(12, 12), false), PointWithState(Point(6, 12), false)}));
}

TEST(POLYGON_CLIPPING_TEST,
Expand All @@ -25,11 +28,16 @@ TEST(POLYGON_CLIPPING_TEST,
std::shared_ptr<Polygon> polygon_ptr1 = std::make_shared<Polygon>(polygon1);
std::shared_ptr<Polygon> polygon_ptr2 = std::make_shared<Polygon>(polygon2);
PolygonClipping polygon_clipping(polygon_ptr1, polygon_ptr2);
std::vector<Point> points;
std::vector<PointWithState> points;

polygon_clipping.CreateVertexList(points, polygon_ptr2, polygon_ptr1);

ASSERT_EQ(points, std::vector<Point>({Point(5, 4), Point(4, 4), Point(3, 4), Point(3, 5), Point(3, 6), Point(2, 6),
Point(2, 5), Point(2, 1), Point(2, 0), Point(3, 0), Point(3, 1), Point(3, 2),
Point(4, 2), Point(5, 2)}));
ASSERT_EQ(points,
std::vector<PointWithState>({PointWithState(Point(5, 4), false), PointWithState(Point(4, 4), true),
PointWithState(Point(3, 4), true), PointWithState(Point(3, 5), true),
PointWithState(Point(3, 6), false), PointWithState(Point(2, 6), false),
PointWithState(Point(2, 5), true), PointWithState(Point(2, 1), true),
PointWithState(Point(2, 0), false), PointWithState(Point(3, 0), false),
PointWithState(Point(3, 1), true), PointWithState(Point(3, 2), true),
PointWithState(Point(4, 2), true), PointWithState(Point(5, 2), false)}));
}
Loading

0 comments on commit b16e5c6

Please sign in to comment.