Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cbad818

Browse files
committed
Use single implementation, add tests
1 parent 3882382 commit cbad818

File tree

4 files changed

+137
-86
lines changed

4 files changed

+137
-86
lines changed

src/geometry/convex-hull.md

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ collinear points, otherwise the algorithm would get the nearest point in this
3737
line and bail. This step shouldn't be included in the non-collinear version
3838
of the algorithm, otherwise you wouldn't get the smallest convex hull.
3939

40-
### Implementation (excluding collinear points)
40+
### Implementation
4141

4242
```cpp graham_scan
4343
struct pt {
@@ -51,49 +51,13 @@ int orientation(pt a, pt b, pt c) {
5151
return 0;
5252
}
5353

54-
bool cw(pt a, pt b, pt c) { return orientation(a, b, c) < 0; }
55-
56-
void convex_hull(vector<pt>& a) {
57-
pt p0 = *min_element(a.begin(), a.end(), [](pt a, pt b) {
58-
return make_pair(a.y, a.x) < make_pair(b.y, b.x);
59-
});
60-
sort(a.begin(), a.end(), [&p0](const pt& a, const pt& b) {
61-
int o = orientation(p0, a, b);
62-
if (o == 0)
63-
return (p0.x-a.x)*(p0.x-a.x) + (p0.y-a.y)*(p0.y-a.y)
64-
< (p0.x-b.x)*(p0.x-b.x) + (p0.y-b.y)*(p0.y-b.y);
65-
return o < 0;
66-
});
67-
68-
vector<pt> st;
69-
for (int i = 0; i < (int)a.size(); i++) {
70-
while (st.size() > 1 && !cw(st[st.size()-2], st.back(), a[i]))
71-
st.pop_back();
72-
st.push_back(a[i]);
73-
}
74-
75-
a = st;
76-
}
77-
```
78-
79-
### Implementation (including collinear points)
80-
81-
```cpp graham_scan_with_collinear
82-
struct pt {
83-
double x, y;
84-
};
85-
86-
int orientation(pt a, pt b, pt c) {
87-
double v = a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y);
88-
if (v < 0) return -1; // clockwise
89-
if (v > 0) return +1; // counter-clockwise
90-
return 0;
54+
bool cw(pt a, pt b, pt c, bool include_collinear) {
55+
int o = orientation(a, b, c);
56+
return o < 0 || (include_collinear && o == 0);
9157
}
92-
93-
bool ccw(pt a, pt b, pt c) { return orientation(a, b, c) > 0; }
9458
bool collinear(pt a, pt b, pt c) { return orientation(a, b, c) == 0; }
9559

96-
void convex_hull(vector<pt>& a) {
60+
void convex_hull(vector<pt>& a, bool include_collinear = false) {
9761
pt p0 = *min_element(a.begin(), a.end(), [](pt a, pt b) {
9862
return make_pair(a.y, a.x) < make_pair(b.y, b.x);
9963
});
@@ -104,13 +68,15 @@ void convex_hull(vector<pt>& a) {
10468
< (p0.x-b.x)*(p0.x-b.x) + (p0.y-b.y)*(p0.y-b.y);
10569
return o < 0;
10670
});
107-
int i = (int)a.size()-1;
108-
while (i >= 0 && collinear(p0, a[i], a.back())) i--;
109-
reverse(a.begin()+i+1, a.end());
71+
if (include_collinear) {
72+
int i = (int)a.size()-1;
73+
while (i >= 0 && collinear(p0, a[i], a.back())) i--;
74+
reverse(a.begin()+i+1, a.end());
75+
}
11076

11177
vector<pt> st;
11278
for (int i = 0; i < (int)a.size(); i++) {
113-
while (st.size() > 1 && ccw(st[st.size()-2], st.back(), a[i]))
79+
while (st.size() > 1 && !cw(st[st.size()-2], st.back(), a[i], include_collinear))
11480
st.pop_back();
11581
st.push_back(a[i]);
11682
}
@@ -164,10 +130,16 @@ int orientation(pt a, pt b, pt c) {
164130
return 0;
165131
}
166132

167-
bool cw(pt a, pt b, pt c) { return orientation(a, b, c) < 0; }
168-
bool ccw(pt a, pt b, pt c) { return orientation(a, b, c) > 0; }
133+
bool cw(pt a, pt b, pt c, bool include_collinear) {
134+
int o = orientation(a, b, c);
135+
return o < 0 || (include_collinear && o == 0);
136+
}
137+
bool ccw(pt a, pt b, pt c, bool include_collinear) {
138+
int o = orientation(a, b, c);
139+
return o > 0 || (include_collinear && o == 0);
140+
}
169141

170-
void convex_hull(vector<pt>& a) {
142+
void convex_hull(vector<pt>& a, bool include_collinear = false) {
171143
if (a.size() == 1)
172144
return;
173145

@@ -179,13 +151,13 @@ void convex_hull(vector<pt>& a) {
179151
up.push_back(p1);
180152
down.push_back(p1);
181153
for (int i = 1; i < (int)a.size(); i++) {
182-
if (i == a.size() - 1 || cw(p1, a[i], p2)) {
183-
while (up.size() >= 2 && !cw(up[up.size()-2], up[up.size()-1], a[i]))
154+
if (i == a.size() - 1 || cw(p1, a[i], p2, include_collinear)) {
155+
while (up.size() >= 2 && !cw(up[up.size()-2], up[up.size()-1], a[i], include_collinear))
184156
up.pop_back();
185157
up.push_back(a[i]);
186158
}
187-
if (i == a.size() - 1 || ccw(p1, a[i], p2)) {
188-
while (down.size() >= 2 && !ccw(down[down.size()-2], down[down.size()-1], a[i]))
159+
if (i == a.size() - 1 || ccw(p1, a[i], p2, include_collinear)) {
160+
while (down.size() >= 2 && !ccw(down[down.size()-2], down[down.size()-1], a[i], include_collinear))
189161
down.pop_back();
190162
down.push_back(a[i]);
191163
}

test/data/convex_hull.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
struct pt { double x, y; };
2+
3+
struct ConvexHull {
4+
vector<pt> points;
5+
vector<pt> convex_hull;
6+
vector<pt> convex_hull_collinear;
7+
};
8+
9+
vector<ConvexHull> convex_hulls = {
10+
// 1 |1 2 1 |1 2
11+
// 0 |0 * 3 0 |0 4 3
12+
// y +----- y +-----
13+
// x 0 1 2 x 0 1 2
14+
{{{0,0},{1,0},{2,0},{0,1},{2,1}},
15+
{{0,0},{0,1},{2,1},{2,0}},
16+
{{0,0},{0,1},{2,1},{2,0},{1,0}}},
17+
{{{0,3},{1,1},{2,2},{4,4},{0,0},{1,2},{3,1},{3,3}},
18+
{{0,0},{0,3},{4,4},{3,1}},
19+
{{0,0},{0,3},{4,4},{3,1}}},
20+
{{{0,0},{0,8},{1,6},{3,1},{6,6},{8,0},{8,8}},
21+
{{0,0},{0,8},{8,8},{8,0}},
22+
{{0,0},{0,8},{8,8},{8,0}}},
23+
{{{2,6},{3,2},{6,6},{0,0},{0,11},{1,1},{1,9},{7,1},{7,9},{8,10},{8,0}},
24+
{{0,0},{0,11},{8,10},{8,0}},
25+
{{0,0},{0,11},{8,10},{8,0}}},
26+
{{{0,0},{0,1},{1,0},{1,1}},
27+
{{0,0},{0,1},{1,1},{1,0}},
28+
{{0,0},{0,1},{1,1},{1,0}}},
29+
{{{0,0},{1,0},{2,0},{2,1},{0,1}},
30+
{{0,0},{0,1},{2,1},{2,0}},
31+
{{0,0},{0,1},{2,1},{2,0},{1,0}}},
32+
{{{101,100},{102,100},{103,100},{104,100},{105,100},{106,100},{107,100},{108,100},{109,100},{110,100},{110,101},{110,102},{110,103},{110,104},{110,105},{110,106},{110,107},{110,108},{110,109},{110,110},{109,110},{108,110},{107,110},{106,110},{105,110},{104,110},{103,110},{102,110},{101,110},{100,110},{100,109},{100,108},{100,107},{100,106},{100,105},{100,104},{100,103},{100,102},{100,101},{100,100}},
33+
{{100,100},{100,110},{110,110},{110,100}},
34+
{{100,100},{100,101},{100,102},{100,103},{100,104},{100,105},{100,106},{100,107},{100,108},{100,109},{100,110},{101,110},{102,110},{103,110},{104,110},{105,110},{106,110},{107,110},{108,110},{109,110},{110,110},{110,109},{110,108},{110,107},{110,106},{110,105},{110,104},{110,103},{110,102},{110,101},{110,100},{109,100},{108,100},{107,100},{106,100},{105,100},{104,100},{103,100},{102,100},{101,100}}},
35+
{{{56,57},{43,58},{53,50},{59,60},{55,44},{39,38},{37,50},{53,46},{61,50},{38,63},{46,55},{46,50},{56,43},{45,50},{45,56},{52,47},{40,61},{55,56},{57,42},{60,61},{54,55},{57,58},{58,50},{52,53},{56,50},{44,43},{62,50},{58,41},{42,59},{44,57},{60,39},{41,40},{55,50},{47,54},{43,42},{58,59},{44,50},{40,50},{38,37},{60,50},{46,45},{54,50},{62,63},{40,39},{43,50},{63,50},{61,62},{41,60},{62,37},{47,46},{41,50},{38,50},{47,50},{42,41},{45,44},{61,38},{48,53},{48,47},{59,40},{59,50},{57,50},{42,50},{54,45},{39,50},{39,62},{53,54}},
36+
{{38,37},{37,50},{38,63},{62,63},{63,50},{62,37}},
37+
{{38,37},{37,50},{38,63},{62,63},{63,50},{62,37}}},
38+
{{{52,49},{88,100},{142,55},{64,61},{82,100},{130,133},{151,100},{121,100},{151,154},{121,124},{91,112},{142,145},{124,100},{67,64},{124,73},{112,85},{118,121},{112,115},{145,148},{55,100},{79,100},{55,52},{46,100},{130,100},{67,136},{109,88},{76,73},{82,79},{43,100},{88,115},{115,82},{109,112},{133,136},{88,85},{94,91},{49,46},{61,58},{85,82},{151,46},{130,67},{148,49},{157,100},{145,100},{154,100},{154,157},{139,142},{91,88},{67,100},{70,133},{127,100},{73,130},{64,139},{112,100},{139,100},{94,109},{82,121},{121,76},{142,100},{136,139},{115,118},{148,100},{76,100},{79,124},{85,100},{76,127},{139,58},{109,100},{118,79},{46,157},{55,148},{136,100},{70,67},{136,61},{61,100},{133,64},{91,100},{49,100},{145,52},{118,100},{73,100},{52,151},{52,100},{70,100},{85,118},{58,145},{127,70},{61,142},{148,151},{46,43},{115,100},{79,76},{64,100},{49,154},{58,100},{106,91},{106,109},{73,70},{124,127},{154,43},{58,55},{133,100},{127,130}},
39+
{{46,43},{43,100},{46,157},{154,157},{157,100},{154,43}},
40+
{{46,43},{43,100},{46,157},{154,157},{157,100},{154,43}}},
41+
{{{100,92},{95,100},{101,100},{86,100},{100,93},{114,100},{100,95},{108,100},{99,100},{100,111},{115,100},{100,108},{100,102},{100,91},{103,100},{100,99},{100,86},{100,107},{91,100},{100,103},{113,100},{100,97},{100,112},{100,104},{92,100},{100,94},{100,109},{98,100},{90,100},{88,100},{100,110},{100,106},{100,105},{100,85},{100,87},{111,100},{97,100},{85,100},{105,100},{100,101},{100,89},{100,96},{100,115},{104,100},{107,100},{94,100},{100,114},{100,88},{110,100},{100,113},{93,100},{100,90},{100,98},{89,100},{102,100},{96,100},{106,100},{109,100},{112,100},{87,100}},
42+
{{100,85},{85,100},{100,115},{115,100}},
43+
{{100,85},{85,100},{100,115},{115,100}}},
44+
{{{100,88},{110,100},{96,100},{134,100},{90,100},{84,100},{100,80},{104,100},{80,100},{100,72},{100,122},{100,110},{140,100},{100,134},{100,98},{54,100},{100,82},{100,94},{108,100},{116,100},{100,140},{112,100},{100,64},{92,100},{128,100},{138,100},{100,60},{100,128},{100,68},{100,74},{120,100},{100,102},{100,136},{100,132},{58,100},{76,100},{130,100},{124,100},{100,76},{100,96},{142,100},{106,100},{100,86},{146,100},{100,126},{100,58},{100,84},{100,106},{100,146},{114,100},{100,90},{60,100},{100,124},{100,104},{100,92},{144,100},{100,62},{100,108},{136,100},{62,100},{100,114},{100,78},{100,54},{100,118},{102,100},{78,100},{88,100},{100,116},{74,100},{100,138},{98,100},{118,100},{82,100},{132,100},{94,100},{122,100},{66,100},{100,56},{100,112},{86,100},{126,100},{100,144},{68,100},{64,100},{100,66},{100,142},{56,100},{72,100},{100,120},{100,130},{70,100},{100,70}},
45+
{{100,54},{54,100},{100,146},{146,100}},
46+
{{100,54},{54,100},{100,146},{146,100}}},
47+
{{{4,7},{4,6}, {4,5}, {4,4}, {4,3}, {4,1}, {4,0}, {4,9}, {4,8}, {5,10}, {2,0}, {2,3}, {2,2}, {2,5}, {2,4}, {2,7}, {2,6}, {2,9}, {2,8}, {0,3}, {0,2}, {0,1}, {0,0}, {0,7}, {0,5}, {0,4}, {0,9}, {10,10}, {5,1}, {9,2}, {9,0}, {9,1}, {9,6}, {9,7}, {9,4}, {0,10}, {9,8}, {9,9}, {10,9}, {10,8}, {10,7}, {10,5}, {10,3}, {10,2}, {10,1}, {7,9}, {7,4}, {7,5}, {7,6}, {7,7}, {7,0}, {7,1}, {7,2}, {7,3}, {2,10}, {6,10}, {5,8}, {5,9}, {5,6}, {5,4}, {5,2}, {5,3}, {5,0}, {8,10}, {3,9}, {3,1}, {3,2}, {3,3}, {3,4}, {3,5}, {3,6}, {3,7}, {1,9}, {1,2}, {1,3}, {1,0}, {1,1}, {1,6}, {1,7}, {1,4}, {1,10}, {4,10}, {9,10}, {9,5}, {7,10}, {8,9}, {8,3}, {8,7}, {8,6}, {8,5}, {8,4}, {6,5}, {6,4}, {6,0}, {6,3}, {6,2}, {6,9}, {6,8}, {3,10}},
48+
{{0,0},{0,10},{10,10},{10,1},{9,0}},
49+
{{0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,7},{0,9},{0,10},{1,10},{2,10},{3,10},{4,10},{5,10},{6,10},{7,10},{8,10},{9,10},{10,10},{10,9},{10,8},{10,7},{10,5},{10,3},{10,2},{10,1},{9,0},{7,0},{6,0},{5,0},{4,0},{2,0},{1,0}}}
50+
};

test/test_convex_hull.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <vector>
2+
#include <algorithm>
3+
#include <cassert>
4+
#include <iostream>
5+
using namespace std;
6+
7+
namespace GrahamScan {
8+
#include "graham_scan.h"
9+
}
10+
namespace MonotoneChain {
11+
#include "monotone_chain.h"
12+
}
13+
#include "data/convex_hull.h"
14+
15+
int main() {
16+
for (auto& [points, ans, ans_col] : convex_hulls) {
17+
// for searching valid rotations
18+
ans.insert(ans.end(), ans.begin(), ans.end());
19+
ans_col.insert(ans_col.end(), ans_col.begin(), ans_col.end());
20+
21+
{
22+
using namespace GrahamScan;
23+
vector<GrahamScan::pt> hull, hull_col;
24+
for (auto p : points) {
25+
hull.push_back({ .x = p.x, .y = p.y });
26+
hull_col.push_back({ .x = p.x, .y = p.y });
27+
}
28+
29+
convex_hull(hull, false);
30+
assert(hull.size() == ans.size()/2);
31+
assert(search(ans.begin(), ans.end(), hull.begin(), hull.end(), [](::pt a, GrahamScan::pt b){
32+
return a.x == b.x && a.y == b.y;
33+
}) != ans.end());
34+
35+
convex_hull(hull_col, true);
36+
assert(hull_col.size() == ans_col.size()/2);
37+
assert(search(ans_col.begin(), ans_col.end(), hull_col.begin(), hull_col.end(), [](::pt a, GrahamScan::pt b){
38+
return a.x == b.x && a.y == b.y;
39+
}) != ans_col.end());
40+
}
41+
42+
{
43+
using namespace MonotoneChain;
44+
vector<MonotoneChain::pt> hull, hull_col;
45+
for (auto p : points) {
46+
hull.push_back({ .x = p.x, .y = p.y });
47+
hull_col.push_back({ .x = p.x, .y = p.y });
48+
}
49+
50+
convex_hull(hull, false);
51+
assert(hull.size() == ans.size()/2);
52+
assert(search(ans.begin(), ans.end(), hull.begin(), hull.end(), [](::pt a, MonotoneChain::pt b){
53+
return a.x == b.x && a.y == b.y;
54+
}) != ans.end());
55+
56+
convex_hull(hull_col, true);
57+
assert(hull_col.size() == ans_col.size()/2);
58+
assert(search(ans_col.begin(), ans_col.end(), hull_col.begin(), hull_col.end(), [](::pt a, MonotoneChain::pt b){
59+
return a.x == b.x && a.y == b.y;
60+
}) != ans_col.end());
61+
}
62+
}
63+
}

test/text_graham_scan.cpp

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)