-
Notifications
You must be signed in to change notification settings - Fork 0
/
graham_scan.cpp
198 lines (177 loc) · 4.35 KB
/
graham_scan.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include "graham_scan.hpp"
/* initialize the list of points */
GrahamScan::GrahamScan()
{
this->pts = new std::vector<Point>;
this->hull = new std::vector<Point>;
this->seen = new std::unordered_set<Point, PointHash, PointComparator>;
}
/*
* This constructor is primarily used for unit testing.
*
* Note: I did try using the Catch2 framework, but I continued
* to get bad_alloc errors. I am unsure why this was happenning.
*
* I then ran tests manually, and there were no errors.
*/
GrahamScan::GrahamScan(std::vector<Point> * pts)
{
this->pts = pts;
this->hull = new std::vector<Point>;
this->seen = new std::unordered_set<Point, PointHash, PointComparator>;
}
/*
* void findHull()
*
*
* Performs a Graham Scan on the set of points
* `pts'. This is done by first sorting the points
* based on y-coordinate. Ties are broken by considering
* the x coordinate.
*
* Once the "lowest" point is found, we can
* sort the remaining points based on angle made with
* the vertical line going through the lowest point.
*
* return
* None
*/
void
GrahamScan::findHull()
{
/* ensure that there are points to consider */
if (pts->empty()) return;
int N = pts->size();
if (N < MIN_POLYGON_VERTICES) return;
/* find the lowest point in the hull */
Point lowest = (*pts)[0];
int idx = 0;
for (int i = 1; i < N; ++i) {
Point curr = (*pts)[i];
if (curr.y < lowest.y) {
lowest = curr;
idx = i;
} else if (
fabs(curr.y-lowest.y) < std::numeric_limits<float>::epsilon()
) {
if (curr.x < lowest.x) {
lowest = curr;
idx = i;
}
}
}
/* the lowest point is in the hull */
hull->push_back(lowest);
/* swap the lowest for the 0th entry */
Point tmp = (*pts)[0];
(*pts)[0] = (*pts)[idx];
(*pts)[idx] = tmp;
/* sort the remaining points according to polar angle with lowest*/
std::sort(pts->begin() + 1, pts->end(), AngleComparator((*hull)[0]));
/* we can be sure that the next point will be in the hull */
Point nextLowest = (*pts)[1];
hull->push_back(nextLowest);
for (int i = 2; i < N; ++i) {
/* as far as we know the i-th point is in the hull */
Point curr = (*pts)[i];
hull->push_back(curr);
while (!leftTurn()) {
/* if a right turn, the second to last element in hull */
int j = hull->size() - 1;
(*hull)[j-1] = (*hull)[j];
/* get rid of duplicate */
hull->pop_back();
}
}
}
/*
* void addPoint(x, y)
*
* addPoint will place the point with
* x-coordinate: x and y-coordinate: y
* into the collection of points.
*
* return
* None
*/
void
GrahamScan::addPoint(int x, int y)
{
Point newPoint;
newPoint.x = x;
newPoint.y = y;
if (seen->count(newPoint) == 0) {
pts->push_back(newPoint);
seen->insert(newPoint);
}
}
/*
* bool leftTurn()
*
* This method will determine if the last 3 points in
* the hull form a left turn. If so, true is returned. False
* otherwise.
*
* We can check if we have a left turn by considering
* the cross product of the last three points in the hull A, B, C
* if AB x AC is positive, then we have made a left turn. Otherwise,
* we have made a right turn.
*
* return
* boolean
*/
bool
GrahamScan::leftTurn()
{
int i = hull->size()-1;
Point c = (*hull)[i];
Point b = (*hull)[i-1];
Point a = (*hull)[i-2];
/* we only need the z-component of cross product */
int ab_x = b.x - a.x;
int ab_y = b.y - a.y;
int ac_x = c.x - a.x;
int ac_y = c.y - a.y;
int z_comp = ab_x * ac_y - ab_y * ac_x;
return z_comp > 0;
}
/*
* vector<Point> & getPoints()
*
* A getter for the points of the graham scan
*
* return
* reference to vector of points
*/
std::vector<Point> &
GrahamScan::getPoints()
{
return *pts;
}
/*
* vector<Point> & getHull()
*
* A getter for the hull of the graham scan
*
* return
* reference to vector of points
*/
std::vector<Point> &
GrahamScan::getHull()
{
return *hull;
}
/*
* void clear()
*
* Method to clear the points and the hull
*
* return
* None
*/
void
GrahamScan::clear()
{
pts->clear();
hull->clear();
}